1 /****************************************************************************
2 ** Copyright (c) 2001-2014
3 **
4 ** This file is part of the QuickFIX FIX Engine
5 **
6 ** This file may be distributed under the terms of the quickfixengine.org
7 ** license as defined by quickfixengine.org and appearing in the file
8 ** LICENSE included in the packaging of this file.
9 **
10 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
11 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
12 **
13 ** See http://www.quickfixengine.org/LICENSE for licensing information.
14 **
15 ** Contact ask@quickfixengine.org if any conditions of this licensing are
16 ** not clear to you.
17 **
18 ****************************************************************************/
19
20 #ifdef _MSC_VER
21 #include "stdafx.h"
22 #else
23 #include "config.h"
24 #endif
25
26 #include "FileStore.h"
27 #include "SessionID.h"
28 #include "Parser.h"
29 #include "Utility.h"
30 #include <fstream>
31
32 namespace FIX
33 {
FileStore(std::string path,const SessionID & s)34 FileStore::FileStore( std::string path, const SessionID& s )
35 : m_msgFile( 0 ), m_headerFile( 0 ), m_seqNumsFile( 0 ), m_sessionFile( 0 )
36 {
37 file_mkdir( path.c_str() );
38
39 if ( path.empty() ) path = ".";
40 const std::string& begin =
41 s.getBeginString().getString();
42 const std::string& sender =
43 s.getSenderCompID().getString();
44 const std::string& target =
45 s.getTargetCompID().getString();
46 const std::string& qualifier =
47 s.getSessionQualifier();
48
49 std::string sessionid = begin + "-" + sender + "-" + target;
50 if( qualifier.size() )
51 sessionid += "-" + qualifier;
52
53 std::string prefix
54 = file_appendpath(path, sessionid + ".");
55
56 m_msgFileName = prefix + "body";
57 m_headerFileName = prefix + "header";
58 m_seqNumsFileName = prefix + "seqnums";
59 m_sessionFileName = prefix + "session";
60
61 try
62 {
63 open( false );
64 }
65 catch ( IOException & e )
66 {
67 throw ConfigError( e.what() );
68 }
69 }
70
~FileStore()71 FileStore::~FileStore()
72 {
73 if( m_msgFile ) fclose( m_msgFile );
74 if( m_headerFile ) fclose( m_headerFile );
75 if( m_seqNumsFile ) fclose( m_seqNumsFile );
76 if( m_sessionFile ) fclose( m_sessionFile );
77 }
78
open(bool deleteFile)79 void FileStore::open( bool deleteFile )
80 {
81 if ( m_msgFile ) fclose( m_msgFile );
82 if ( m_headerFile ) fclose( m_headerFile );
83 if ( m_seqNumsFile ) fclose( m_seqNumsFile );
84 if ( m_sessionFile ) fclose( m_sessionFile );
85
86 m_msgFile = 0;
87 m_headerFile = 0;
88 m_seqNumsFile = 0;
89 m_sessionFile = 0;
90
91 if ( deleteFile )
92 {
93 file_unlink( m_msgFileName.c_str() );
94 file_unlink( m_headerFileName.c_str() );
95 file_unlink( m_seqNumsFileName.c_str() );
96 file_unlink( m_sessionFileName.c_str() );
97 }
98
99 populateCache();
100 m_msgFile = file_fopen( m_msgFileName.c_str(), "r+" );
101 if ( !m_msgFile ) m_msgFile = file_fopen( m_msgFileName.c_str(), "w+" );
102 if ( !m_msgFile ) throw ConfigError( "Could not open body file: " + m_msgFileName );
103
104 m_headerFile = file_fopen( m_headerFileName.c_str(), "r+" );
105 if ( !m_headerFile ) m_headerFile = file_fopen( m_headerFileName.c_str(), "w+" );
106 if ( !m_headerFile ) throw ConfigError( "Could not open header file: " + m_headerFileName );
107
108 m_seqNumsFile = file_fopen( m_seqNumsFileName.c_str(), "r+" );
109 if ( !m_seqNumsFile ) m_seqNumsFile = file_fopen( m_seqNumsFileName.c_str(), "w+" );
110 if ( !m_seqNumsFile ) throw ConfigError( "Could not open seqnums file: " + m_seqNumsFileName );
111
112 bool setCreationTime = false;
113 m_sessionFile = file_fopen( m_sessionFileName.c_str(), "r" );
114 if ( !m_sessionFile ) setCreationTime = true;
115 else fclose( m_sessionFile );
116
117 m_sessionFile = file_fopen( m_sessionFileName.c_str(), "r+" );
118 if ( !m_sessionFile ) m_sessionFile = file_fopen( m_sessionFileName.c_str(), "w+" );
119 if ( !m_sessionFile ) throw ConfigError( "Could not open session file" );
120 if ( setCreationTime ) setSession();
121
122 setNextSenderMsgSeqNum( getNextSenderMsgSeqNum() );
123 setNextTargetMsgSeqNum( getNextTargetMsgSeqNum() );
124 }
125
populateCache()126 void FileStore::populateCache()
127 {
128 FILE* headerFile = file_fopen( m_headerFileName.c_str(), "r+" );
129 if ( headerFile )
130 {
131 int num;
132 long offset;
133 std::size_t size;
134
135 while (FILE_FSCANF(headerFile, "%d,%ld,%lu ", &num, &offset, &size) == 3)
136 {
137 std::pair<NumToOffset::iterator, bool> it =
138 m_offsets.insert(NumToOffset::value_type(num, std::make_pair(offset, size)));
139 //std::cout << it.first->second.first << " --- " << it.first->second.second << '\n';
140 if (it.second == false)
141 {
142 it.first->second = std::make_pair(offset, size);
143 }
144 }
145 fclose( headerFile );
146 }
147
148 FILE* seqNumsFile = file_fopen( m_seqNumsFileName.c_str(), "r+" );
149 if ( seqNumsFile )
150 {
151 int sender, target;
152 if ( FILE_FSCANF( seqNumsFile, "%d : %d", &sender, &target ) == 2 )
153 {
154 m_cache.setNextSenderMsgSeqNum( sender );
155 m_cache.setNextTargetMsgSeqNum( target );
156 }
157 fclose( seqNumsFile );
158 }
159
160 FILE* sessionFile = file_fopen( m_sessionFileName.c_str(), "r+" );
161 if ( sessionFile )
162 {
163 char time[ 22 ];
164 #ifdef HAVE_FSCANF_S
165 int result = FILE_FSCANF( sessionFile, "%s", time, 22 );
166 #else
167 int result = FILE_FSCANF( sessionFile, "%s", time );
168 #endif
169 if( result == 1 )
170 {
171 m_cache.setCreationTime( UtcTimeStampConvertor::convert( time ) );
172 }
173 fclose( sessionFile );
174 }
175 }
176
create(const SessionID & s)177 MessageStore* FileStoreFactory::create( const SessionID& s )
178 {
179 if ( m_path.size() ) return new FileStore( m_path, s );
180
181 std::string path;
182 Dictionary settings = m_settings.get( s );
183 path = settings.getString( FILE_STORE_PATH );
184 return new FileStore( path, s );
185 }
186
destroy(MessageStore * pStore)187 void FileStoreFactory::destroy( MessageStore* pStore )
188 {
189 delete pStore;
190 }
191
set(int msgSeqNum,const std::string & msg)192 bool FileStore::set( int msgSeqNum, const std::string& msg )
193 throw ( IOException )
194 {
195 if ( fseek( m_msgFile, 0, SEEK_END ) )
196 throw IOException( "Cannot seek to end of " + m_msgFileName );
197 if ( fseek( m_headerFile, 0, SEEK_END ) )
198 throw IOException( "Cannot seek to end of " + m_headerFileName );
199
200 long offset = ftell( m_msgFile );
201 if ( offset < 0 )
202 throw IOException( "Unable to get file pointer position from " + m_msgFileName );
203 std::size_t size = msg.size();
204
205 if ( fprintf( m_headerFile, "%d,%ld,%lu ", msgSeqNum, offset, size ) < 0 )
206 throw IOException( "Unable to write to file " + m_headerFileName );
207 std::pair<NumToOffset::iterator, bool> it =
208 m_offsets.insert(NumToOffset::value_type(msgSeqNum, std::make_pair(offset, size)));
209 if (it.second == false)
210 {
211 it.first->second = std::make_pair(offset, size);
212 }
213 fwrite( msg.c_str(), sizeof( char ), msg.size(), m_msgFile );
214 if ( ferror( m_msgFile ) )
215 throw IOException( "Unable to write to file " + m_msgFileName );
216 if ( fflush( m_msgFile ) == EOF )
217 throw IOException( "Unable to flush file " + m_msgFileName );
218 if ( fflush( m_headerFile ) == EOF )
219 throw IOException( "Unable to flush file " + m_headerFileName );
220 return true;
221 }
222
get(int begin,int end,std::vector<std::string> & result) const223 void FileStore::get( int begin, int end,
224 std::vector < std::string > & result ) const
225 throw ( IOException )
226 {
227 result.clear();
228 std::string msg;
229 for ( int i = begin; i <= end; ++i )
230 {
231 if ( get( i, msg ) )
232 result.push_back( msg );
233 }
234 }
235
getNextSenderMsgSeqNum() const236 int FileStore::getNextSenderMsgSeqNum() const throw ( IOException )
237 {
238 return m_cache.getNextSenderMsgSeqNum();
239 }
240
getNextTargetMsgSeqNum() const241 int FileStore::getNextTargetMsgSeqNum() const throw ( IOException )
242 {
243 return m_cache.getNextTargetMsgSeqNum();
244 }
245
setNextSenderMsgSeqNum(int value)246 void FileStore::setNextSenderMsgSeqNum( int value ) throw ( IOException )
247 {
248 m_cache.setNextSenderMsgSeqNum( value );
249 setSeqNum();
250 }
251
setNextTargetMsgSeqNum(int value)252 void FileStore::setNextTargetMsgSeqNum( int value ) throw ( IOException )
253 {
254 m_cache.setNextTargetMsgSeqNum( value );
255 setSeqNum();
256 }
257
incrNextSenderMsgSeqNum()258 void FileStore::incrNextSenderMsgSeqNum() throw ( IOException )
259 {
260 m_cache.incrNextSenderMsgSeqNum();
261 setSeqNum();
262 }
263
incrNextTargetMsgSeqNum()264 void FileStore::incrNextTargetMsgSeqNum() throw ( IOException )
265 {
266 m_cache.incrNextTargetMsgSeqNum();
267 setSeqNum();
268 }
269
getCreationTime() const270 UtcTimeStamp FileStore::getCreationTime() const throw ( IOException )
271 {
272 return m_cache.getCreationTime();
273 }
274
reset()275 void FileStore::reset() throw ( IOException )
276 {
277 try
278 {
279 m_cache.reset();
280 open( true );
281 setSession();
282 }
283 catch( std::exception& e )
284 {
285 throw IOException( e.what() );
286 }
287 }
288
refresh()289 void FileStore::refresh() throw ( IOException )
290 {
291 try
292 {
293 m_cache.reset();
294 open( false );
295 }
296 catch( std::exception& e )
297 {
298 throw IOException( e.what() );
299 }
300 }
301
setSeqNum()302 void FileStore::setSeqNum()
303 {
304 rewind( m_seqNumsFile );
305 fprintf( m_seqNumsFile, "%10.10d : %10.10d",
306 getNextSenderMsgSeqNum(), getNextTargetMsgSeqNum() );
307 if ( ferror( m_seqNumsFile ) )
308 throw IOException( "Unable to write to file " + m_seqNumsFileName );
309 if ( fflush( m_seqNumsFile ) )
310 throw IOException( "Unable to flush file " + m_seqNumsFileName );
311 }
312
setSession()313 void FileStore::setSession()
314 {
315 rewind( m_sessionFile );
316 fprintf( m_sessionFile, "%s",
317 UtcTimeStampConvertor::convert( m_cache.getCreationTime() ).c_str() );
318 if ( ferror( m_sessionFile ) )
319 throw IOException( "Unable to write to file " + m_sessionFileName );
320 if ( fflush( m_sessionFile ) )
321 throw IOException( "Unable to flush file " + m_sessionFileName );
322 }
323
get(int msgSeqNum,std::string & msg) const324 bool FileStore::get( int msgSeqNum, std::string& msg ) const
325 throw ( IOException )
326 {
327 NumToOffset::const_iterator find = m_offsets.find( msgSeqNum );
328 if ( find == m_offsets.end() ) return false;
329 const OffsetSize& offset = find->second;
330 if ( fseek( m_msgFile, offset.first, SEEK_SET ) )
331 throw IOException( "Unable to seek in file " + m_msgFileName );
332 char* buffer = new char[ offset.second + 1 ];
333 size_t result = fread( buffer, sizeof( char ), offset.second, m_msgFile );
334 if ( ferror( m_msgFile ) || result != (size_t)offset.second )
335 {
336 delete [] buffer;
337 throw IOException( "Unable to read from file " + m_msgFileName );
338 }
339 buffer[ offset.second ] = 0;
340 msg = buffer;
341 delete [] buffer;
342 return true;
343 }
344
345 } //namespace FIX
346