/* * Copyright 2003,2004,2005 Colin Crist * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package hermes.fix.quickfix; import hermes.HermesRuntimeException; import hermes.fix.FIXMessage; import hermes.fix.NoSuchFieldException; import java.io.UnsupportedEncodingException; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.apache.log4j.Logger; import org.apache.mina.filter.codec.ProtocolDecoderOutput; import quickfix.DataDictionary; import quickfix.Field; import quickfix.FieldNotFound; import quickfix.InvalidMessage; import quickfix.Message; import quickfix.field.MsgSeqNum; import quickfix.field.MsgType; import quickfix.field.OnBehalfOfCompID; import quickfix.field.OnBehalfOfSubID; import quickfix.field.SenderCompID; import quickfix.field.SendingTime; import quickfix.field.TargetCompID; import quickfix.mina.message.FIXMessageDecoder; /** * This abstract FIX message uses a cache to hold all releated Java objects once * the message has been marshalled. When the marshalled message is evicted from * the cache, the reset() method is called letting this message perform * additional cleanup. * * @author colincrist@hermesjms.com * @version $Id: AbstractQuickFIXMessage.java,v 1.5 2007/02/28 10:47:27 * colincrist Exp $ */ public abstract class AbstractQuickFIXMessage implements FIXMessage { private static Set<Integer> retainedFields; private Lock lock = new ReentrantLock(); static { retainedFields = new HashSet<Integer>(); retainedFields.add(SenderCompID.FIELD); retainedFields.add(TargetCompID.FIELD); retainedFields.add(SendingTime.FIELD); retainedFields.add(MsgSeqNum.FIELD); retainedFields.add(MsgType.FIELD); retainedFields.add(OnBehalfOfCompID.FIELD); retainedFields.add(OnBehalfOfSubID.FIELD); } private static final Logger log = Logger.getLogger(AbstractQuickFIXMessage.class); class MyDecoderOutput implements ProtocolDecoderOutput { public void write(Object object) { try { getCache().put(AbstractQuickFIXMessage.this, new Message((String) object)); } catch (Exception ex) { log.error(ex.getMessage(), ex); throw new HermesRuntimeException(ex); } } public void flush() { // TODO Auto-generated method stub } } public AbstractQuickFIXMessage(QuickFIXMessageCache cache) { this.cache = cache; } public QuickFIXMessageCache getCache() { return cache; } private volatile Map<Integer, Field> allFields; private volatile Map<Integer, Field> cachedFields = new HashMap<Integer, Field>(); private DataDictionary dictionary; private QuickFIXMessageCache cache; private FIXMessageDecoder getDecoder() throws UnsupportedEncodingException { return cache.getDecoder(); } public String getMsgType() { return getString(MsgType.FIELD); } public synchronized Set<Integer> getFieldOrder() { return getAllFields().keySet(); } public boolean fieldExists(int tag) { if (cachedFields.containsKey(tag)) { return true; } return getAllFields().containsKey(tag); } public Map<Integer, Field> getAllFields() { if (allFields == null) { try { final Message message = getMessage(); lock.lock(); allFields = new LinkedHashMap<Integer, Field>(); if (message == null) { return new HashMap<Integer, Field>(); } for (final Iterator iterator = message.getHeader().iterator(); iterator.hasNext();) { Field field = (Field) iterator.next(); allFields.put(field.getTag(), field); if (retainedFields.contains(field.getTag())) { cachedFields.put(field.getTag(), field); } } for (final Iterator iterator = message.iterator(); iterator.hasNext();) { Field field = (Field) iterator.next(); int tag = field.getTag(); if (!allFields.containsKey(tag)) { allFields.put(tag, field); if (retainedFields.contains(field.getTag())) { cachedFields.put(field.getTag(), field); } } } for (Iterator groupsKeys = message.groupKeyIterator(); groupsKeys.hasNext();) { int groupCountTag = ((Integer) groupsKeys.next()).intValue(); } for (final Iterator iterator = message.getTrailer().iterator(); iterator.hasNext();) { Field field = (Field) iterator.next(); allFields.put(field.getTag(), field); if (retainedFields.contains(field.getTag())) { cachedFields.put(field.getTag(), field); } } } finally { lock.unlock(); } } return allFields; } public void reset() { lock.lock(); try { if (allFields != null) { allFields.clear(); } allFields = null; } finally { lock.unlock(); } } public Object getObject(Field field) { if (cachedFields.containsKey(field)) { return cachedFields.get(field).getObject(); } Field cached = getAllFields().get(field.getTag()); if (cached != null) { return cached.getObject(); } else { return null; } } public Object getObject(int tag) throws NoSuchFieldException { if (cachedFields.containsKey(tag)) { return getObject(cachedFields.get(tag)); } if (getAllFields().containsKey(tag)) { return getObject(getAllFields().get(tag)); } else { throw new NoSuchFieldException(tag); } } public String getString(int field) { try { if (cachedFields.containsKey(field)) { return cachedFields.get(field).getObject().toString(); } if (getAllFields().containsKey(field)) { return getAllFields().get(field).getObject().toString(); } else { throw new FieldNotFound("No such field " + field); } } catch (FieldNotFound e) { throw new HermesRuntimeException(e); } } public DataDictionary getDictionary() { return dictionary; } protected void setDictionary(DataDictionary dictionary) { this.dictionary = dictionary; } public String toString() { return new String(getBytes()); } public Message getMessage() { getCache().lock(); try { if (!getCache().contains(AbstractQuickFIXMessage.this)) { try { Message message = new Message(new String(getBytes()), dictionary, false); getCache().put(this, message); if (getDictionary() == null) { setDictionary(QuickFIXUtils.getDictionary(message)); } return message; } catch (InvalidMessage e) { log.error("Ignoring invalid message: " + e.getMessage(), e); return null; } catch (Exception e) { throw new HermesRuntimeException(e); } } else { return getCache().get(this); } } finally { getCache().unlock(); } } }