/* * JBoss, Home of Professional Open Source * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated * by the @authors tag. All rights reserved. * See the copyright.txt in the distribution for a * full listing of individual contributors. * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions * of the GNU Lesser General Public License, v. 2.1. * This program is distributed in the hope that it will be useful, but WITHOUT A * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public License, * v.2.1 along with this distribution; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ package org.savara.monitor; import java.util.logging.Level; import java.util.logging.Logger; import org.savara.protocol.ProtocolCriteria.Direction; import org.savara.protocol.ProtocolId; import org.savara.protocol.export.monitor.ForkJoinMonitorExportVisitor; import org.savara.protocol.repository.ProtocolRepository; import org.scribble.common.logging.CachedJournal; import org.scribble.protocol.export.monitor.MonitorProtocolExporter; import org.scribble.protocol.model.ProtocolModel; import org.scribble.protocol.monitor.DefaultProtocolMonitor; import org.scribble.protocol.monitor.DefaultSession; import org.scribble.protocol.monitor.MonitorContext; import org.scribble.protocol.monitor.ProtocolMonitor; import org.scribble.protocol.monitor.Result; import org.scribble.protocol.monitor.Session; import org.scribble.protocol.monitor.model.Description; import org.scribble.protocol.monitor.util.MonitorModelUtil; /** * This class provides a default implementation of the * Montor. * */ public class DefaultMonitor implements Monitor { private ProtocolRepository m_protocolRepository=null; private SessionStore m_sessionStore=null; private ConversationResolver m_conversationResolver=null; private ProtocolMonitor m_monitor=new DefaultProtocolMonitor(); private DescriptionCache m_descriptionCache=new DescriptionCache(); private MonitorProtocolExporter m_exporter=new MonitorProtocolExporter(); private MonitorContext _context=new DefautMonitorContext(); private static final Logger logger=Logger.getLogger(DefaultMonitor.class.getName()); /** * Default constructor. */ public DefaultMonitor() { m_exporter.setMonitorExportVisitor(new ForkJoinMonitorExportVisitor()); } /** * This method sets the monitor context. * * @param context The monitor context */ public void setMonitorContext(MonitorContext context) { _context = context; } /** * This method returns the monitor context. * * @return The monitor context */ public MonitorContext getMonitorContext() { return (_context); } /** * This method sets the protocol monitor. * * @param pm The protocol monitor */ public void setProtocolMonitor(ProtocolMonitor pm) { m_monitor = pm; } /** * This method sets the protocol repository to use when * monitoring. * * @param rep The protocol repository */ public void setProtocolRepository(ProtocolRepository rep) { m_protocolRepository = rep; } /** * This method sets the session store to use when * monitoring. * * @param store The session store */ public void setSessionStore(SessionStore store) { m_sessionStore = store; } /** * This method sets the conversation resolver to use when * processing messages against the monitor. * * @param resolver The conversation resolver */ public void setConversationResolver(ConversationResolver resolver) { m_conversationResolver = resolver; } /** * This method is used to indicate that a message has been * sent or received, and should be monitored against the * specified protocol id and optional conversation instance * id. * * If the protocol id is not specified, then the first * relevant protocol will be used. If none are found, then * a null result will be returned. * * If the conversation instance id is not explicitly * specified, then the protocol monitor will be responsible * for deriving the appropriate value. * * @param pid The optional protocol id * @param cid The optional conversation instance id * @param mesg The message * @return The monitor result, or null if a suitable protocol was not found * @throws ProtocolUnknownException Unknown protocol name or role * @throws IOException Failed to create or retrieve session */ public MonitorResult process(ProtocolId pid, ConversationId cid, Message mesg) { MonitorResult ret=null; boolean messageRelevant=false; ConversationId unhandledCID=null; if (m_protocolRepository == null) { throw new IllegalStateException("Protocol repository has not been configured"); } else if (m_sessionStore == null) { throw new IllegalStateException("Session store has not been configured"); } if (pid == null) { // Find the protocol ids relevant for this message java.util.List<ProtocolId> pids=m_protocolRepository.getProtocols(mesg); if (pids != null) { for (ProtocolId pi : pids) { Description desc=getProtocolDescription(pi); ConversationId lcid=cid; // Check if conversation instance id should be derived if (lcid == null && m_conversationResolver != null) { lcid = m_conversationResolver.getConversationId(desc, mesg); } if (lcid != null) { messageRelevant = true; unhandledCID = lcid; Result result=processProtocol(pi, lcid, desc, mesg); if (result != null && result != Result.NOT_HANDLED) { ret = new MonitorResult(pi, lcid, result.isValid(), result.getReason(), result.getProperties()); break; } } } } else if (logger.isLoggable(Level.FINEST)) { logger.finest("No protocols found for message '"+mesg+"'"); } } else { Description desc=getProtocolDescription(pid); // Check if conversation instance id should be derived if (cid == null && m_conversationResolver != null) { cid = m_conversationResolver.getConversationId(desc, mesg); } if (cid != null) { messageRelevant = true; Result result=processProtocol(pid, cid, desc, mesg); if (result != null && result != Result.NOT_HANDLED) { ret = new MonitorResult(pid, cid, result.isValid(), result.getReason(), result.getProperties()); } } } if (ret == null) { if (messageRelevant) { if (logger.isLoggable(Level.FINEST)) { logger.finest("Message 'not handled' but relevant: "+mesg); } if (cid == null) { cid = unhandledCID; } ret = new MonitorResult(pid, cid, false, null, null); } else if (logger.isLoggable(Level.FINEST)) { logger.finest("Message 'not handled' or relevant: "+mesg); } } return(ret); } protected Result processProtocol(ProtocolId pid, ConversationId cid, Description desc, Message mesg) { Result ret=null; // If conversation id not available, then must fail monitoring // TODO: HOW SHOULD TECHNICAL FAILURE BE REFLECTED IN ACTIVITY? if (cid == null) { logger.severe("Conversation id not defined for protocol '"+pid+ "' and message '"+mesg+"'"); return(Result.INVALID); } java.io.Serializable session=m_sessionStore.find(pid, cid); boolean f_create=false; if (session == null) { // Try to create new session session = (DefaultSession)m_monitor.createSession(_context, desc, DefaultSession.class); //m_sessionStore.create(pid, cid, session); f_create = true; } if (session instanceof Session) { // Won't specify role, as part of protocol description not // generally in the runtime environment - possible future // enhancement if (mesg.getDirection() == Direction.Outbound) { ret = m_monitor.messageSent(_context, desc, (Session)session, mesg); } else { ret = m_monitor.messageReceived(_context, desc, (Session)session, mesg); } // If session just created but result not handled, or session finished // then remove if (ret != Result.NOT_HANDLED) { if (f_create) { if (!((Session)session).isFinished()) { m_sessionStore.create(pid, cid, session); } } else if (((Session)session).isFinished()) { m_sessionStore.remove(pid, cid); } else { m_sessionStore.update(pid, cid, session); } } /* if ((f_create && ret == Result.NOT_HANDLED) || ((Session)session).isFinished()) { m_sessionStore.remove(pid, cid); } else { m_sessionStore.update(pid, cid, session); } */ } else { logger.severe("Inappropriate session type returned"); ret = Result.NOT_HANDLED; } return(ret); } /** * This method returns the protocol's monitorable description. * * @param pid The protocol id * @return The description */ protected Description getProtocolDescription(ProtocolId pid) { Description ret=getDescriptionCache().getDescription(pid); if (ret == null) { try { ProtocolModel pm=m_protocolRepository.getProtocol(pid); // Convert protocol model to monitoring description CachedJournal journal=new CachedJournal(); java.io.ByteArrayOutputStream os=new java.io.ByteArrayOutputStream(); m_exporter.export(pm, journal, os); os.close(); if (journal.hasErrors()) { logger.severe("Errors detected when exporting protocol '"+ pid+"' to monitorable description"); } else { java.io.InputStream is=new java.io.ByteArrayInputStream(os.toByteArray()); ret = MonitorModelUtil.deserialize(is); is.close(); } } catch(Exception e) { logger.log(Level.SEVERE, "Failed to obtain monitorable description for protocol '"+ pid+"'", e); } if (ret != null) { getDescriptionCache().setDescription(pid, ret); } } return(ret); } protected DescriptionCache getDescriptionCache() { return(m_descriptionCache); } protected void setDescriptionCache(DescriptionCache dc) { m_descriptionCache = dc; } }