/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.camel.component.quickfixj;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import org.apache.camel.CamelContext;
import org.apache.camel.Component;
import org.apache.camel.Consumer;
import org.apache.camel.Exchange;
import org.apache.camel.MultipleConsumersSupport;
import org.apache.camel.Processor;
import org.apache.camel.Producer;
import org.apache.camel.ResolveEndpointFailedException;
import org.apache.camel.component.quickfixj.converter.QuickfixjConverters;
import org.apache.camel.impl.DefaultEndpoint;
import org.apache.camel.spi.Metadata;
import org.apache.camel.spi.UriEndpoint;
import org.apache.camel.spi.UriParam;
import org.apache.camel.spi.UriPath;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import quickfix.Message;
import quickfix.SessionID;
/**
* The quickfix component allows to send Financial Interchange (FIX) messages to the QuickFix engine.
*/
@UriEndpoint(firstVersion = "2.1.0", scheme = "quickfix", title = "QuickFix", syntax = "quickfix:configurationName", consumerClass = QuickfixjConsumer.class, label = "engine,messaging")
public class QuickfixjEndpoint extends DefaultEndpoint implements QuickfixjEventListener, MultipleConsumersSupport {
public static final String EVENT_CATEGORY_KEY = "EventCategory";
public static final String SESSION_ID_KEY = "SessionID";
public static final String MESSAGE_TYPE_KEY = "MessageType";
public static final String DATA_DICTIONARY_KEY = "DataDictionary";
private static final Logger LOG = LoggerFactory.getLogger(QuickfixjEndpoint.class);
private final QuickfixjEngine engine;
private final List<QuickfixjConsumer> consumers = new CopyOnWriteArrayList<QuickfixjConsumer>();
@UriPath @Metadata(required = "true")
private String configurationName;
@UriParam
private SessionID sessionID;
@UriParam
private boolean lazyCreateEngine;
@Deprecated
public QuickfixjEndpoint(QuickfixjEngine engine, String uri, CamelContext context) {
super(uri, context);
this.engine = engine;
}
public QuickfixjEndpoint(QuickfixjEngine engine, String uri, Component component) {
super(uri, component);
this.engine = engine;
}
public SessionID getSessionID() {
return sessionID;
}
/**
* The optional sessionID identifies a specific FIX session. The format of the sessionID is:
* (BeginString):(SenderCompID)[/(SenderSubID)[/(SenderLocationID)]]->(TargetCompID)[/(TargetSubID)[/(TargetLocationID)]]
*/
public void setSessionID(SessionID sessionID) {
this.sessionID = sessionID;
}
public String getConfigurationName() {
return configurationName;
}
/**
* The configFile is the name of the QuickFIX/J configuration to use for the FIX engine (located as a resource found in your classpath).
*/
public void setConfigurationName(String configurationName) {
this.configurationName = configurationName;
}
public boolean isLazyCreateEngine() {
return lazyCreateEngine;
}
/**
* This option allows to create QuickFIX/J engine on demand.
* Value true means the engine is started when first message is send or there's consumer configured in route definition.
* When false value is used, the engine is started at the endpoint creation.
* When this parameter is missing, the value of component's property lazyCreateEngines is being used.
*/
public void setLazyCreateEngine(boolean lazyCreateEngine) {
this.lazyCreateEngine = lazyCreateEngine;
}
@Override
public Consumer createConsumer(Processor processor) throws Exception {
LOG.info("Creating QuickFIX/J consumer: {}, ExchangePattern={}", sessionID != null ? sessionID : "No Session", getExchangePattern());
QuickfixjConsumer consumer = new QuickfixjConsumer(this, processor);
configureConsumer(consumer);
consumers.add(consumer);
return consumer;
}
@Override
public Producer createProducer() throws Exception {
LOG.info("Creating QuickFIX/J producer: {}", sessionID != null ? sessionID : "No Session");
if (isWildcarded()) {
throw new ResolveEndpointFailedException("Cannot create consumer on wildcarded session identifier: " + sessionID);
}
return new QuickfixjProducer(this);
}
@Override
public boolean isSingleton() {
return true;
}
@Override
public void onEvent(QuickfixjEventCategory eventCategory, SessionID sessionID, Message message) throws Exception {
if (this.sessionID == null || isMatching(sessionID)) {
for (QuickfixjConsumer consumer : consumers) {
Exchange exchange = QuickfixjConverters.toExchange(this, sessionID, message, eventCategory, getExchangePattern());
consumer.onExchange(exchange);
if (exchange.getException() != null) {
throw exchange.getException();
}
}
}
}
private boolean isMatching(SessionID sessionID) {
if (this.sessionID.equals(sessionID)) {
return true;
}
return isMatching(this.sessionID.getBeginString(), sessionID.getBeginString())
&& isMatching(this.sessionID.getSenderCompID(), sessionID.getSenderCompID())
&& isMatching(this.sessionID.getSenderSubID(), sessionID.getSenderSubID())
&& isMatching(this.sessionID.getSenderLocationID(), sessionID.getSenderLocationID())
&& isMatching(this.sessionID.getTargetCompID(), sessionID.getTargetCompID())
&& isMatching(this.sessionID.getTargetSubID(), sessionID.getTargetSubID())
&& isMatching(this.sessionID.getTargetLocationID(), sessionID.getTargetLocationID());
}
private boolean isMatching(String s1, String s2) {
return s1.equals("") || s1.equals("*") || s1.equals(s2);
}
private boolean isWildcarded() {
if (sessionID == null) {
return false;
}
return sessionID.getBeginString().equals("*")
|| sessionID.getSenderCompID().equals("*")
|| sessionID.getSenderSubID().equals("*")
|| sessionID.getSenderLocationID().equals("*")
|| sessionID.getTargetCompID().equals("*")
|| sessionID.getTargetSubID().equals("*")
|| sessionID.getTargetLocationID().equals("*");
}
@Override
public boolean isMultipleConsumersSupported() {
return true;
}
/**
* Initializing and starts the engine if it wasn't initialized so far.
*/
public void ensureInitialized() throws Exception {
if (!engine.isInitialized()) {
synchronized (engine) {
if (!engine.isInitialized()) {
engine.initializeEngine();
engine.start();
}
}
}
}
public QuickfixjEngine getEngine() {
return engine;
}
@Override
protected void doStop() throws Exception {
// clear list of consumers
consumers.clear();
}
}