|
@ -1,431 +0,0 @@
|
|
|
/*
|
|
|
* 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 com.yihu.hos.broker.common.log;
|
|
|
|
|
|
import com.yihu.hos.core.datatype.DateUtil;
|
|
|
import com.yihu.hos.core.datatype.StringUtil;
|
|
|
import net.sf.json.JSONObject;
|
|
|
import org.apache.log4j.AppenderSkeleton;
|
|
|
import org.apache.log4j.helpers.LogLog;
|
|
|
import org.apache.log4j.spi.ErrorCode;
|
|
|
import org.apache.log4j.spi.LoggingEvent;
|
|
|
|
|
|
import javax.jms.*;
|
|
|
import javax.naming.Context;
|
|
|
import javax.naming.InitialContext;
|
|
|
import javax.naming.NameNotFoundException;
|
|
|
import javax.naming.NamingException;
|
|
|
import java.util.HashMap;
|
|
|
import java.util.Map;
|
|
|
import java.util.Properties;
|
|
|
|
|
|
/**
|
|
|
* A simple appender that publishes events to a JMS Topic. The events
|
|
|
* are serialized and transmitted as JMS message type {@link
|
|
|
* ObjectMessage}.
|
|
|
* <p>
|
|
|
* <p>JMS {@link Topic topics} and {@link TopicConnectionFactory topic
|
|
|
* connection factories} are administered objects that are retrieved
|
|
|
* using JNDI messaging which in turn requires the retrieval of a JNDI
|
|
|
* {@link Context}.
|
|
|
* <p>
|
|
|
* <p>There are two common methods for retrieving a JNDI {@link
|
|
|
* Context}. If a file resource named <em>jndi.properties</em> is
|
|
|
* available to the JNDI API, it will use the information found
|
|
|
* therein to retrieve an initial JNDI context. To obtain an initial
|
|
|
* context, your code will simply call:
|
|
|
* <p>
|
|
|
* <pre>
|
|
|
* InitialContext jndiContext = new InitialContext();
|
|
|
* </pre>
|
|
|
* <p>
|
|
|
* <p>Calling the no-argument <code>InitialContext()</code> method
|
|
|
* will also work from within Enterprise Java Beans (EJBs) because it
|
|
|
* is part of the EJB contract for application servers to provide each
|
|
|
* bean an environment naming context (ENC).
|
|
|
* <p>
|
|
|
* <p>In the second approach, several predetermined properties are set
|
|
|
* and these properties are passed to the <code>InitialContext</code>
|
|
|
* constructor to connect to the naming service provider. For example,
|
|
|
* to connect to JBoss naming service one would write:
|
|
|
* <p>
|
|
|
* <pre>
|
|
|
* Properties env = new Properties( );
|
|
|
* env.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
|
|
|
* env.put(Context.PROVIDER_URL, "jnp://hostname:1099");
|
|
|
* env.put(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces");
|
|
|
* InitialContext jndiContext = new InitialContext(env);
|
|
|
* </pre>
|
|
|
* <p>
|
|
|
* where <em>hostname</em> is the host where the JBoss application
|
|
|
* server is running.
|
|
|
* <p>
|
|
|
* <p>To connect to the the naming service of Weblogic application
|
|
|
* server one would write:
|
|
|
* <p>
|
|
|
* <pre>
|
|
|
* Properties env = new Properties( );
|
|
|
* env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
|
|
|
* env.put(Context.PROVIDER_URL, "t3://localhost:7001");
|
|
|
* InitialContext jndiContext = new InitialContext(env);
|
|
|
* </pre>
|
|
|
* <p>
|
|
|
* <p>Other JMS providers will obviously require different values.
|
|
|
* <p>
|
|
|
* The initial JNDI context can be obtained by calling the
|
|
|
* no-argument <code>InitialContext()</code> method in EJBs. Only
|
|
|
* clients running in a separate JVM need to be concerned about the
|
|
|
* <em>jndi.properties</em> file and calling {@link
|
|
|
* InitialContext#InitialContext()} or alternatively correctly
|
|
|
* setting the different properties before calling {@link
|
|
|
* InitialContext#InitialContext(java.util.Hashtable)} method.
|
|
|
*
|
|
|
* @author Ceki Gülcü
|
|
|
*/
|
|
|
public class JMSAppender extends AppenderSkeleton {
|
|
|
|
|
|
private String securityPrincipalName;
|
|
|
private String securityCredentials;
|
|
|
private String initialContextFactoryName;
|
|
|
private String urlPkgPrefixes;
|
|
|
private String providerURL;
|
|
|
private String topicBindingName;
|
|
|
private String tcfBindingName;
|
|
|
private String userName;
|
|
|
private String password;
|
|
|
private boolean locationInfo;
|
|
|
|
|
|
private TopicConnection topicConnection;
|
|
|
private TopicSession topicSession;
|
|
|
private TopicPublisher topicPublisher;
|
|
|
|
|
|
public JMSAppender() {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Returns the value of the <b>TopicConnectionFactoryBindingName</b> option.
|
|
|
*/
|
|
|
public String getTopicConnectionFactoryBindingName() {
|
|
|
return tcfBindingName;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* The <b>TopicConnectionFactoryBindingName</b> option takes a
|
|
|
* string value. Its value will be used to lookup the appropriate
|
|
|
* <code>TopicConnectionFactory</code> from the JNDI context.
|
|
|
*/
|
|
|
public void setTopicConnectionFactoryBindingName(String tcfBindingName) {
|
|
|
this.tcfBindingName = tcfBindingName;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Returns the value of the <b>TopicBindingName</b> option.
|
|
|
*/
|
|
|
public String getTopicBindingName() {
|
|
|
return topicBindingName;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* The <b>TopicBindingName</b> option takes a
|
|
|
* string value. Its value will be used to lookup the appropriate
|
|
|
* <code>Topic</code> from the JNDI context.
|
|
|
*/
|
|
|
public void setTopicBindingName(String topicBindingName) {
|
|
|
this.topicBindingName = topicBindingName;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Returns value of the <b>LocationInfo</b> property which
|
|
|
* determines whether location (stack) info is sent to the remote
|
|
|
* subscriber.
|
|
|
*/
|
|
|
public boolean getLocationInfo() {
|
|
|
return locationInfo;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* If true, the information sent to the remote subscriber will
|
|
|
* include caller's location information. By default no location
|
|
|
* information is sent to the subscriber.
|
|
|
*/
|
|
|
public void setLocationInfo(boolean locationInfo) {
|
|
|
this.locationInfo = locationInfo;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Options are activated and become effective only after calling
|
|
|
* this method.
|
|
|
*/
|
|
|
public void activateOptions() {
|
|
|
TopicConnectionFactory topicConnectionFactory;
|
|
|
|
|
|
try {
|
|
|
Context jndi;
|
|
|
|
|
|
LogLog.debug("Getting initial context.");
|
|
|
if (initialContextFactoryName != null) {
|
|
|
Properties env = new Properties();
|
|
|
env.put(Context.INITIAL_CONTEXT_FACTORY, initialContextFactoryName);
|
|
|
if (providerURL != null) {
|
|
|
env.put(Context.PROVIDER_URL, providerURL);
|
|
|
} else {
|
|
|
LogLog.warn("You have set InitialContextFactoryName option but not the "
|
|
|
+ "ProviderURL. This is likely to cause problems.");
|
|
|
}
|
|
|
if (urlPkgPrefixes != null) {
|
|
|
env.put(Context.URL_PKG_PREFIXES, urlPkgPrefixes);
|
|
|
}
|
|
|
|
|
|
if (securityPrincipalName != null) {
|
|
|
env.put(Context.SECURITY_PRINCIPAL, securityPrincipalName);
|
|
|
if (securityCredentials != null) {
|
|
|
env.put(Context.SECURITY_CREDENTIALS, securityCredentials);
|
|
|
} else {
|
|
|
LogLog.warn("You have set SecurityPrincipalName option but not the "
|
|
|
+ "SecurityCredentials. This is likely to cause problems.");
|
|
|
}
|
|
|
}
|
|
|
jndi = new InitialContext(env);
|
|
|
} else {
|
|
|
jndi = new InitialContext();
|
|
|
}
|
|
|
|
|
|
LogLog.debug("Looking up [" + tcfBindingName + "]");
|
|
|
topicConnectionFactory = (TopicConnectionFactory) lookup(jndi, tcfBindingName);
|
|
|
LogLog.debug("About to create TopicConnection.");
|
|
|
if (userName != null) {
|
|
|
topicConnection = topicConnectionFactory.createTopicConnection(userName,
|
|
|
password);
|
|
|
} else {
|
|
|
topicConnection = topicConnectionFactory.createTopicConnection();
|
|
|
}
|
|
|
|
|
|
LogLog.debug("Creating TopicSession, non-transactional, "
|
|
|
+ "in AUTO_ACKNOWLEDGE mode.");
|
|
|
topicSession = topicConnection.createTopicSession(false,
|
|
|
Session.AUTO_ACKNOWLEDGE);
|
|
|
|
|
|
LogLog.debug("Looking up topic name [" + topicBindingName + "].");
|
|
|
Topic topic = (Topic) lookup(jndi, topicBindingName);
|
|
|
|
|
|
LogLog.debug("Creating TopicPublisher.");
|
|
|
topicPublisher = topicSession.createPublisher(topic);
|
|
|
|
|
|
LogLog.debug("Starting TopicConnection.");
|
|
|
topicConnection.start();
|
|
|
|
|
|
jndi.close();
|
|
|
} catch (JMSException | NamingException | RuntimeException e) {
|
|
|
errorHandler.error("Error while activating options for appender named [" + name +
|
|
|
"].", e, ErrorCode.GENERIC_FAILURE);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Close this JMSAppender. Closing releases all resources used by the
|
|
|
* appender. A closed appender cannot be re-opened.
|
|
|
*/
|
|
|
public synchronized void close() {
|
|
|
// The synchronized modifier avoids concurrent append and close operations
|
|
|
|
|
|
if (this.closed)
|
|
|
return;
|
|
|
|
|
|
LogLog.debug("Closing appender [" + name + "].");
|
|
|
this.closed = true;
|
|
|
|
|
|
try {
|
|
|
if (topicSession != null)
|
|
|
topicSession.close();
|
|
|
if (topicConnection != null)
|
|
|
topicConnection.close();
|
|
|
} catch (JMSException | RuntimeException e) {
|
|
|
LogLog.error("Error while closing JMSAppender [" + name + "].", e);
|
|
|
}
|
|
|
// Help garbage collection
|
|
|
topicPublisher = null;
|
|
|
topicSession = null;
|
|
|
topicConnection = null;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* This method called by {@link AppenderSkeleton#doAppend} method to
|
|
|
* do most of the real appending work.
|
|
|
*/
|
|
|
public void append(LoggingEvent event) {
|
|
|
|
|
|
if (!checkEntryConditions()) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
if (StringUtil.isEmpty(event.getMDC("camel.messageId"))) {
|
|
|
return;
|
|
|
}
|
|
|
ObjectMessage msg = topicSession.createObjectMessage();
|
|
|
String message = event.getMessage().toString();
|
|
|
// String body = message.substring(message.indexOf("Body:") + 5);
|
|
|
Map<String, String> map = new HashMap<>();
|
|
|
map.put("exchangeId", StringUtil.toString(event.getMDC("camel.exchangeId")));
|
|
|
map.put("correlationId", StringUtil.toString(event.getMDC("camel.correlationId")));
|
|
|
map.put("transactionKey", StringUtil.toString(event.getMDC("camel.transactionKey")));
|
|
|
map.put("routeId", StringUtil.toString(event.getMDC("camel.routeId")));
|
|
|
map.put("breadcrumbId", StringUtil.toString(event.getMDC("camel.breadcrumbId")));
|
|
|
map.put("camelContextId", StringUtil.toString(event.getMDC("camel.contextId")));
|
|
|
map.put("body", message);
|
|
|
map.put("fireTimeSource", DateUtil.toStringFormatGMTTime(DateUtil.toGMTTime(event.getTimeStamp()), DateUtil.DEFAULT_TIMESTAMP_FORMAT));
|
|
|
msg.setObject(JSONObject.fromObject(map).toString());
|
|
|
|
|
|
topicPublisher.publish(msg);
|
|
|
} catch (JMSException | RuntimeException e) {
|
|
|
errorHandler.error("Could not publish message in JMSAppender [" + name + "].", e,
|
|
|
ErrorCode.GENERIC_FAILURE);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Returns the value of the <b>InitialContextFactoryName</b> option.
|
|
|
* See {@link #setInitialContextFactoryName} for more details on the
|
|
|
* meaning of this option.
|
|
|
*/
|
|
|
public String getInitialContextFactoryName() {
|
|
|
return initialContextFactoryName;
|
|
|
}
|
|
|
|
|
|
public void setInitialContextFactoryName(String initialContextFactoryName) {
|
|
|
this.initialContextFactoryName = initialContextFactoryName;
|
|
|
}
|
|
|
|
|
|
public String getProviderURL() {
|
|
|
return providerURL;
|
|
|
}
|
|
|
|
|
|
public void setProviderURL(String providerURL) {
|
|
|
this.providerURL = providerURL;
|
|
|
}
|
|
|
|
|
|
public String getSecurityCredentials() {
|
|
|
return securityCredentials;
|
|
|
}
|
|
|
|
|
|
public void setSecurityCredentials(String securityCredentials) {
|
|
|
this.securityCredentials = securityCredentials;
|
|
|
}
|
|
|
|
|
|
public String getSecurityPrincipalName() {
|
|
|
return securityPrincipalName;
|
|
|
}
|
|
|
|
|
|
public void setSecurityPrincipalName(String securityPrincipalName) {
|
|
|
this.securityPrincipalName = securityPrincipalName;
|
|
|
}
|
|
|
|
|
|
public String getUserName() {
|
|
|
return userName;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* The user name to use when {@link
|
|
|
* TopicConnectionFactory#createTopicConnection(String, String)
|
|
|
* creating a topic session}. If you set this option, you should
|
|
|
* also set the <b>Password</b> option. See {@link
|
|
|
* #setPassword(String)}.
|
|
|
*/
|
|
|
public void setUserName(String userName) {
|
|
|
this.userName = userName;
|
|
|
}
|
|
|
|
|
|
public String getPassword() {
|
|
|
return password;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* The paswword to use when creating a topic session.
|
|
|
*/
|
|
|
public void setPassword(String password) {
|
|
|
this.password = password;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* The JMSAppender sends serialized events and consequently does not
|
|
|
* require a layout.
|
|
|
*/
|
|
|
public boolean requiresLayout() {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
protected Object lookup(Context ctx, String name) throws NamingException {
|
|
|
try {
|
|
|
return ctx.lookup(name);
|
|
|
} catch (NameNotFoundException e) {
|
|
|
LogLog.error("Could not find name [" + name + "].");
|
|
|
throw e;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
protected boolean checkEntryConditions() {
|
|
|
String fail = null;
|
|
|
|
|
|
if (this.topicConnection == null) {
|
|
|
fail = "No TopicConnection";
|
|
|
} else if (this.topicSession == null) {
|
|
|
fail = "No TopicSession";
|
|
|
} else if (this.topicPublisher == null) {
|
|
|
fail = "No TopicPublisher";
|
|
|
}
|
|
|
|
|
|
if (fail != null) {
|
|
|
errorHandler.error(fail + " for JMSAppender named [" + name + "].");
|
|
|
return false;
|
|
|
} else {
|
|
|
return true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Returns the TopicConnection used for this appender. Only valid after
|
|
|
* activateOptions() method has been invoked.
|
|
|
*/
|
|
|
protected TopicConnection getTopicConnection() {
|
|
|
return topicConnection;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Returns the TopicSession used for this appender. Only valid after
|
|
|
* activateOptions() method has been invoked.
|
|
|
*/
|
|
|
protected TopicSession getTopicSession() {
|
|
|
return topicSession;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Returns the TopicPublisher used for this appender. Only valid after
|
|
|
* activateOptions() method has been invoked.
|
|
|
*/
|
|
|
protected TopicPublisher getTopicPublisher() {
|
|
|
return topicPublisher;
|
|
|
}
|
|
|
|
|
|
String getURLPkgPrefixes() {
|
|
|
return urlPkgPrefixes;
|
|
|
}
|
|
|
|
|
|
public void setURLPkgPrefixes(String urlPkgPrefixes) {
|
|
|
this.urlPkgPrefixes = urlPkgPrefixes;
|
|
|
}
|
|
|
}
|