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