/* * 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.tuscany.sca.implementation.guardian.impl; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import org.apache.axiom.om.OMAttribute; import org.apache.axiom.om.OMElement; import org.apache.tuscany.sca.contribution.service.ContributionReadException; import org.apache.tuscany.sca.implementation.guardian.common.InvalidRegularExpressionException; import org.apache.tuscany.sca.implementation.guardian.GuardianGroup; import org.apache.tuscany.sca.implementation.guardian.GuardianMember; import org.apache.tuscany.sca.implementation.guardian.common.ConcurrentExceptionOcurrenceException; import org.apache.tuscany.sca.implementation.guardian.common.Constants; import org.apache.tuscany.sca.implementation.guardian.common.Context; import org.apache.tuscany.sca.implementation.guardian.common.GlobalException; import org.apache.tuscany.sca.implementation.guardian.common.GlobalExceptionInterface; import org.apache.tuscany.sca.implementation.guardian.common.InvalidNodeException; import org.apache.tuscany.sca.implementation.guardian.common.ResolutionTreeUtils; import org.apache.tuscany.sca.implementation.guardian.common.SuspendException; import org.apache.tuscany.sca.implementation.guardian.xml.RecoveryRulesProcessor; import org.apache.tuscany.sca.implementation.guardian.xml.ResolutionTreesProcessor; public class GuardianGroupImpl implements GuardianGroup { private List guardianList; private InnerGuardianGroupThread innerThread; private List concurrentExList; private Map resolutionTreeElements; private ResolutionTreeUtils resolutionTreeUtils; private Map ruleElements; public GuardianGroupImpl(String recoveryRules, String resolutionTrees) { guardianList = new LinkedList(); concurrentExList = new LinkedList(); innerThread = new InnerGuardianGroupThread(); resolutionTreeUtils = new ResolutionTreeUtils(); setRecoveryRules(recoveryRules); setResolutionTree(resolutionTrees); } private void setRecoveryRules(String recoveryRules) { try { FileInputStream fileInputStream = new FileInputStream(recoveryRules); XMLStreamReader xmlReader = XMLInputFactory.newInstance().createXMLStreamReader(fileInputStream); RecoveryRulesProcessor processor = new RecoveryRulesProcessor(); ruleElements = processor.read(xmlReader).getRuleElements(); } catch (ContributionReadException ex) { Logger.getLogger(GuardianGroupImpl.class.getName()).log(Level.SEVERE, null, ex); } catch (XMLStreamException ex) { Logger.getLogger(GuardianGroupImpl.class.getName()).log(Level.SEVERE, null, ex); } catch (FileNotFoundException ex) { Logger.getLogger(GuardianGroupImpl.class.getName()).log(Level.SEVERE, null, ex); } } private void setResolutionTree(String resolutionTree) { try { FileInputStream fileInputStream = new FileInputStream(resolutionTree); XMLStreamReader resolutionTreeReader = XMLInputFactory.newInstance().createXMLStreamReader(fileInputStream); ResolutionTreesProcessor processor = new ResolutionTreesProcessor(); resolutionTreeElements = processor.read(resolutionTreeReader).getResolutionTreeElements(); } catch (ContributionReadException ex) { Logger.getLogger(GuardianGroupImpl.class.getName()).log(Level.SEVERE, null, ex); } catch (XMLStreamException ex) { Logger.getLogger(GuardianGroupImpl.class.getName()).log(Level.SEVERE, null, ex); } catch (FileNotFoundException ex) { Logger.getLogger(GuardianGroupImpl.class.getName()).log(Level.SEVERE, null, ex); } } public void addGuardianMember(GuardianMember guardianMember) { guardianList.add(guardianMember); //guardianMember.setUniqueParticipantID(guardianList.size() - 1); } public boolean removeGuardianMember(GuardianMember guardianMember) { return guardianList.remove(guardianMember); } public void enableContext(Context context) { throw new UnsupportedOperationException("Not supported yet."); } public Context removeContext() { throw new UnsupportedOperationException("Not supported yet."); } public synchronized void gthrow(GlobalExceptionInterface ex, List participantList) { concurrentExList.add(ex); //Sends a message representing the exception to the other guardian members SuspendException suspendEx = new SuspendException(); suspendEx.putSignalingParticipant(ex.getSignalingParticipants().get(0)); if (participantList == null) { for (GuardianMember g : guardianList) { g.gthrow(suspendEx, null); } } else { for (GuardianMember g : guardianList) { if (participantList.contains(g.getParticipantIdentifier()) || g.getParticipantIdentifier().equals(ex.getSignalingParticipants().get(0))) { g.gthrow(suspendEx, null); } } } if (!innerThread.isRunning()) { innerThread.setGlobalException(ex); new Thread(innerThread).start(); } } public boolean propagate(GlobalExceptionInterface ex) { throw new UnsupportedOperationException("Not supported yet."); } public void checkExceptionStatus() { throw new UnsupportedOperationException("Not supported yet."); } private class InnerGuardianGroupThread implements Runnable { private boolean isRunning; private GlobalExceptionInterface ex = null; public InnerGuardianGroupThread() { isRunning = false; } public void setGlobalException(GlobalExceptionInterface ex) { this.ex = ex; } public GlobalExceptionInterface getGlobalException() { return ex; } public void run() { isRunning = true; if (ex != null) { //Time window of 10 seconds just for tests try { Thread.sleep(10000); } catch (InterruptedException ex1) { Logger.getLogger(GuardianGroupImpl.class.getName()).log(Level.SEVERE, null, ex1); } try { applyRecoveryRules(ex); } catch (ConcurrentExceptionOcurrenceException ce) { applyConcurrentRecoveryRules(); } //Clear the concurrent exception list concurrentExList.clear(); //Set up the participant state to NORMAL for (GuardianMember gm : guardianList) { if (gm.getParticipantState() == SUSPENDED_PARTICIPANT_STATE) { gm.setParticipantState(NORMAL_PARTICIPANT_STATE); } } } isRunning = false; } public boolean isRunning() { return isRunning; } private void applyRecoveryRules(GlobalExceptionInterface ex) throws ConcurrentExceptionOcurrenceException { ruleTag(ex, ruleElements.values().iterator()); } private void ruleTag(GlobalExceptionInterface ex, Iterator ruleElements) throws ConcurrentExceptionOcurrenceException { String signaledException; String exceptionName; OMElement rule; while (ruleElements.hasNext()) { rule = ruleElements.next(); signaledException = getAttributeValue(rule, Constants.SIGNALED_EXCEPTION); exceptionName = ex.getClass().getName(); if (signaledException.equals(exceptionName)) { participantExceptionTag(ex, rule.getChildElements()); break; } } } private void participantExceptionTag(GlobalExceptionInterface ex, Iterator participantElements) throws ConcurrentExceptionOcurrenceException { String matchParticipant; List gmList; OMElement participant; while (participantElements.hasNext()) { participant = participantElements.next(); matchParticipant = getAttributeValue(participant, Constants.MATCH); gmList = getMatchingParticipants(matchParticipant, ex); if (!gmList.isEmpty()) { throwExceptionTag(gmList, ex, participant.getChildElements()); } } } private void throwExceptionTag(List gmList, GlobalExceptionInterface ex, Iterator throwExceptionElements) throws ConcurrentExceptionOcurrenceException { String className; String targetContextName; Integer minParticipantJoined; Integer maxParticipantJoined; OMElement throwException; while (throwExceptionElements.hasNext()) { throwException = throwExceptionElements.next(); className = getAttributeValue(throwException, Constants.CLASS); targetContextName = getAttributeValue(throwException, Constants.TARGET_CONTEXT); try { minParticipantJoined = Integer.parseInt(getAttributeValue(throwException, Constants.MIN_PARTICIPANT_JOINED)); } catch (NumberFormatException nex) { minParticipantJoined = null; } try { maxParticipantJoined = Integer.parseInt(getAttributeValue(throwException, Constants.MAX_PARTICIPANT_JOINED)); } catch (NumberFormatException nexc) { maxParticipantJoined = null; } //Test the min and max joined participants condition if (minParticipantJoined != null && maxParticipantJoined != null) { if (!(guardianList.size() >= minParticipantJoined && guardianList.size() < maxParticipantJoined)) { break; } } else if (minParticipantJoined != null) { if (!(guardianList.size() >= minParticipantJoined)) { break; } } else if (minParticipantJoined != null) { if (!(guardianList.size() >= minParticipantJoined)) { break; } } // String affectedParticipants = affectedParticipantsTag(throwException.getChildElements()); int index = -1; //Verify if the parameter is an index try { index = Integer.parseInt(affectedParticipants); } catch (NumberFormatException nexc) { index = -1; } //Create the new exception instance Class exceptionClass; try { exceptionClass = Class.forName(className); Context targetContext; if (targetContextName.toUpperCase().equals(Context.CURRENT_CONTEXT.getName().toUpperCase())) { targetContext = Context.CURRENT_CONTEXT; } else if (targetContextName.toUpperCase().equals(Context.INIT_CONTEXT.getName().toUpperCase())) { targetContext = Context.INIT_CONTEXT; } else { targetContext = new Context(targetContextName); } GlobalException newException = (GlobalException) exceptionClass.newInstance(); newException.setTargetContext(targetContext); newException.setSignalingContext(ex.getSignalingContext()); newException.putSignalingParticipant(ex.getSignalingParticipants().toString()); //Check concurrent exception existence if (concurrentExList.size() > 1) { throw new ConcurrentExceptionOcurrenceException(concurrentExList.toString()); } //Add the exception to the participants matched if (index != -1) { gmList.get(index).addException(newException); } else if (affectedParticipants != null && affectedParticipants.length() != 0) { if (affectedParticipants.toUpperCase().equals(Constants.FIRST)) { gmList.get(0).addException(newException); } else if (affectedParticipants.toUpperCase().equals(Constants.LAST)) { gmList.get(gmList.size() - 1).addException(newException); } else if (affectedParticipants.toUpperCase().equals(Constants.ALL)) { for (GuardianMember gm : gmList) { gm.addException(newException); } } } else { for (GuardianMember gm : gmList) { gm.addException(newException); } } } catch (InstantiationException ex1) { Logger.getLogger(GuardianGroupImpl.class.getName()).log(Level.SEVERE, null, ex1); } catch (IllegalAccessException ex1) { Logger.getLogger(GuardianGroupImpl.class.getName()).log(Level.SEVERE, null, ex1); } catch (ClassNotFoundException ex1) { Logger.getLogger(GuardianGroupImpl.class.getName()).log(Level.SEVERE, null, ex1); } } } private String affectedParticipantsTag(Iterator affectedParticipantElements) { String affectedParticipantValue = null; OMElement affectedParticipant; while (affectedParticipantElements.hasNext()) { affectedParticipant = affectedParticipantElements.next(); affectedParticipantValue = affectedParticipant.getText(); } if (affectedParticipantValue != null && affectedParticipantValue.length() == 0) { affectedParticipantValue = null; } return affectedParticipantValue; } private String getAttributeValue(OMElement element, String attributeName) { OMAttribute at; Iterator it = element.getAllAttributes(); while (it.hasNext()) { at = (OMAttribute) it.next(); if (at.getLocalName().equals(attributeName)) { return at.getAttributeValue(); } } return null; } private void applyConcurrentRecoveryRules() { boolean concurrentExOcurrence = false; List copyConcurrentExList; GlobalExceptionInterface resolvedEx; do { System.out.println("Concurrent exceptions: " + concurrentExList); copyConcurrentExList = new LinkedList(concurrentExList); resolvedEx = checkExceptionResolutionTrees(copyConcurrentExList, resolutionTreeElements.values().iterator()); concurrentExList.clear(); System.out.println("Resolved Exception: " + resolvedEx); if (resolvedEx != null) { System.out.println("List of participants: " + resolvedEx.getSignalingParticipants()); } try { //Process the exception list sequentially if (resolvedEx == null) { for (GlobalExceptionInterface ex : copyConcurrentExList) { applyRecoveryRules(ex); } } else { applyRecoveryRules(resolvedEx); } } catch (ConcurrentExceptionOcurrenceException exc) { concurrentExOcurrence = true; break; } } while (concurrentExOcurrence); } //FIXME: Need to check the exception level private GlobalExceptionInterface checkExceptionResolutionTrees(List exceptionList, Iterator resolutionTreesElements) { OMElement tree; OMElement root; String exceptionLevel = null; GlobalExceptionInterface resolvedEx = null; while (resolutionTreesElements.hasNext()) { tree = (OMElement) resolutionTreesElements.next(); exceptionLevel = tree.getAttributeValue(Constants.EXCEPTION_LEVEL_QNAME); root = (OMElement) tree.getChildElements().next(); resolvedEx = checkExceptionResolutionTree(exceptionList, root); if (resolvedEx != null) { break; } } return resolvedEx; } //Search for the root of the smallest subtree that contains all the concurrently signaled exceptions. If not found, return null. private GlobalExceptionInterface checkExceptionResolutionTree(List exceptionList, OMElement rootTree) { resolutionTreeUtils.setRoot(rootTree); String ex1, ex2; GlobalExceptionInterface resolvedEx = null; ex1 = exceptionList.get(0).getClass().getName(); for (int i = 1; i < exceptionList.size(); i++) { ex2 = exceptionList.get(i).getClass().getName(); try { ex1 = resolutionTreeUtils.getLowestCommonAncestor(ex1, ex2); } catch (InvalidNodeException invalidNodeException) { ex1 = null; break; } } if (ex1 != null) { Class exceptionClass; try { exceptionClass = Class.forName(ex1); resolvedEx = (GlobalException) exceptionClass.newInstance(); for (GlobalExceptionInterface ex : exceptionList) { resolvedEx.putSignalingParticipant(ex.getSignalingParticipants().get(0)); } } catch (InstantiationException ex) { Logger.getLogger(GuardianGroupImpl.class.getName()).log(Level.SEVERE, null, ex); } catch (IllegalAccessException ex) { Logger.getLogger(GuardianGroupImpl.class.getName()).log(Level.SEVERE, null, ex); } catch (ClassNotFoundException ex) { Logger.getLogger(GuardianGroupImpl.class.getName()).log(Level.SEVERE, null, ex); } return resolvedEx; } else { return null; } } private List getMatchingParticipants(String regularExpression, GlobalExceptionInterface signaledException) { List matchingParticipants = new LinkedList(); String[] splitByComma = regularExpression.split(","); //Invalid regular expression if (splitByComma.length > 2) { throw new InvalidRegularExpressionException("The comma ',' operator can only be applied for two expressions"); } //There is no comma on the regular expression if (splitByComma.length == 1) { if (regularExpression.toUpperCase().equals("SIGNALER")) { for (GuardianMember gm : guardianList) { if (signaledException.getSignalingParticipants().contains(gm.getParticipantIdentifier())) { matchingParticipants.add(gm); } } } else if (regularExpression.toUpperCase().equals("!SIGNALER")) { for (GuardianMember gm : guardianList) { if (!signaledException.getSignalingParticipants().contains(gm.getParticipantIdentifier())) { matchingParticipants.add(gm); } } } else { //Create an java regular expression String re = createJavaRegularExpression(regularExpression); for (GuardianMember gm : guardianList) { if (gm.getParticipantIdentifier().matches(re)) { matchingParticipants.add(gm); } } } } //There is comma on the regular expression else { String element; int index = -1; for (int i = 0; i < splitByComma.length; i++) { element = splitByComma[i].toUpperCase(); if (element.equals("SIGNALER") || element.equals("!SIGNALER")) { if (index == -1) { index = i; } else { index = -1; } } } //Invalid expression if (index == -1) { throw new InvalidRegularExpressionException("The comma ',' requires a SIGNALER or !SIGNALER element in one side of the expression"); } String re = createJavaRegularExpression(splitByComma[1 - index]); for (GuardianMember gm : guardianList) { //Test if the participant matches with the regular expression if (gm.getParticipantIdentifier().matches(re)) { //Test if the participant is Signaler if (splitByComma[index].toUpperCase().equals("SIGNALER")) { if (signaledException.getSignalingParticipants().contains(gm.getParticipantIdentifier())) { matchingParticipants.add(gm); } } //Test if the participant is not Signaler else { if (!signaledException.getSignalingParticipants().contains(gm.getParticipantIdentifier())) { matchingParticipants.add(gm); } } } } } return matchingParticipants; } /* Valid expressions: *, .*, , *., *..*, * *..*..*, || * * Invalid expressions: *.*, **, * * Not supported yet: !, ! || , !( || ) */ private String createJavaRegularExpression(String regularExpression) throws InvalidRegularExpressionException { StringBuffer re = new StringBuffer(); String[] splitByBar = regularExpression.split("\\|\\|"); String[] splitByPeriod; for (int i = 0; i < splitByBar.length; i++) { splitByPeriod = splitByBar[i].split("\\."); if (i > 0) { re.append("|"); } re.append("^"); for (int j = 0; j < splitByPeriod.length; j++) { //* if (splitByPeriod[j].equals("*")) { //Validate the regular expression if (j + 1 != splitByPeriod.length && splitByPeriod[j + 1].equals("*")) { throw new InvalidRegularExpressionException(); } //* if (splitByPeriod.length == 1) { re.append("(\\w+)"); } //*. if (j == 0 && splitByPeriod.length != 1) { re.append("(\\w+\\"); re.append(".)*"); } //.* else { re.append("(\\"); re.append(".\\w+)*"); } } // else { // //Validate the regular expression // if (splitedByPeriod[j].matches("^(\\*)*$")) { // throw new RuntimeException("Invalid name for a context"); // } // || .. || *. if (splitByPeriod.length == 1) { re.append("(\\w+\\"); re.append(".)*"); } if (j == 0 || j - 1 == 0) { re.append("(" + splitByPeriod[j] + ")"); } else { re.append("(\\." + splitByPeriod[j] + ")"); } } } re.append("$"); } return re.toString(); } } }