/* * 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.guardian; import org.apache.tuscany.sca.contribution.service.ContributionReadException; import org.apache.tuscany.sca.policy.resolutiontrees.ResolutionTreesPolicyProcessor; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.lang.annotation.Annotation; 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.axis2.jaxws.message.util.ResettableReader; import org.osoa.sca.annotations.Property; import org.osoa.sca.annotations.Scope; import org.osoa.sca.annotations.Service; @Service(GuardianGroup.class) @Scope("COMPOSITE") public class GuardianGroupImpl implements GuardianGroup { private List guardianList; private ResettableReader recoveryRulesReader; private InnerGuardianGroupThread innerThread; private List concurrentExList; private Map resolutionTreeElements; public GuardianGroupImpl() { guardianList = new LinkedList(); concurrentExList = new LinkedList(); innerThread = new InnerGuardianGroupThread(); } @Property(name = "recovery_rules", required = false) public void setRecoveryRules(String recoveryRules) { try { FileInputStream fileInputStream = new FileInputStream(recoveryRules); XMLStreamReader xmlReader = XMLInputFactory.newInstance().createXMLStreamReader(fileInputStream); recoveryRulesReader = new ResettableReader(xmlReader); } 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); } } @Property(name = "resolution_tree", required = false) public void setResolutionTree(String resolutionTree) { try { FileInputStream fileInputStream = new FileInputStream(resolutionTree); XMLStreamReader resolutionTreeReader = XMLInputFactory.newInstance().createXMLStreamReader(fileInputStream); ResolutionTreesPolicyProcessor processor = new ResolutionTreesPolicyProcessor(null, null); 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 void enableContext(Context context) { System.out.println("Enable Context.. nothing to do!"); //throw new UnsupportedOperationException("Not supported yet."); } public void 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."); } public boolean removeGuardianMember(GuardianMember guardianMember) { return this.guardianList.remove(guardianMember); } 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 { recoveryRulesReader.reset(); ruleTag(ex); } private void ruleTag(GlobalExceptionInterface ex) throws ConcurrentExceptionOcurrenceException { try { while (recoveryRulesReader.hasNext()) { recoveryRulesReader.next(); // if (recoveryRulesReader.isStartElement() && recoveryRulesReader.getLocalName().equals(Constants.RULE)) { for (int i = 0; i < recoveryRulesReader.getAttributeCount(); i++) { //ex == signaled_exception if (recoveryRulesReader.getAttributeLocalName(i).equals(Constants.SIGNALED_EXCEPTION) && ex.getClass().getName().equals(recoveryRulesReader.getAttributeValue(i))) { participantExceptionTag(ex); break; } } } } } catch (XMLStreamException exc) { Logger.getLogger(GuardianGroupImpl.class.getName()).log(Level.SEVERE, null, ex); } } private void participantExceptionTag(GlobalExceptionInterface ex) throws ConcurrentExceptionOcurrenceException { List gmList; try { while (recoveryRulesReader.hasNext() && !(recoveryRulesReader.isEndElement() && recoveryRulesReader.getLocalName().equals(Constants.RULE))) { recoveryRulesReader.next(); // if (recoveryRulesReader.isStartElement() && recoveryRulesReader.getLocalName().equals(Constants.PARTICIPANT)) { String participantMatch = recoveryRulesReader.getAttributeValue(0).trim(); gmList = getMatchingParticipants(participantMatch, ex); //TESTING-------------------------------------------- for (GuardianMember gm : gmList) { System.out.println(gm.getParticipantIdentifier()); } //--------------------------------------------------- if (!gmList.isEmpty()) { throwExceptionTag(gmList, ex); } } } } catch (XMLStreamException exc) { Logger.getLogger(GuardianGroupImpl.class.getName()).log(Level.SEVERE, null, exc); } } private void throwExceptionTag(List gmList, GlobalExceptionInterface ex) throws ConcurrentExceptionOcurrenceException { String exceptionClassName; String targetContextName; Integer min_participant_joined; Integer max_participant_joined; try { while (recoveryRulesReader.hasNext() && !(recoveryRulesReader.isEndElement() && recoveryRulesReader.getLocalName().equals(Constants.PARTICIPANT))) { recoveryRulesReader.next(); // if (recoveryRulesReader.isStartElement() && recoveryRulesReader.getLocalName().equals(Constants.THROW_EXCEPTION)) { exceptionClassName = null; targetContextName = null; min_participant_joined = null; max_participant_joined = null; for (int j = 0; j < recoveryRulesReader.getAttributeCount(); j++) { if (recoveryRulesReader.getAttributeLocalName(j).equals(Constants.CLASS)) { //class value exceptionClassName = recoveryRulesReader.getAttributeValue(j); } else if (recoveryRulesReader.getAttributeLocalName(j).equals(Constants.TARGET_CONTEXT)) { //target_context value targetContextName = recoveryRulesReader.getAttributeValue(j); } else if (recoveryRulesReader.getAttributeLocalName(j).equals(Constants.MIN_PARTICIPANT_JOINED)) { //min_participant_joined value min_participant_joined = Integer.parseInt(recoveryRulesReader.getAttributeValue(j)); } else { //max_participant_joined value max_participant_joined = Integer.parseInt(recoveryRulesReader.getAttributeValue(j)); } } //Test the min and max joined participants condition if (min_participant_joined != null && max_participant_joined != null) { if (!(guardianList.size() >= min_participant_joined && guardianList.size() < max_participant_joined)) { break; } } else if (min_participant_joined != null) { if (!(guardianList.size() >= min_participant_joined)) { break; } } else if (max_participant_joined != null) { if (!(guardianList.size() >= min_participant_joined)) { break; } } // String affectedParticipants = affectedParticipantsTag(); int index = -1; //Verify if the parameter is an index try { index = Integer.parseInt(affectedParticipants); } catch (NumberFormatException nexc) { index = -1; } Class exceptionClass = Class.forName(exceptionClassName); 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 (XMLStreamException ex1) { Logger.getLogger(GuardianGroupImpl.class.getName()).log(Level.SEVERE, null, ex1); } 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() { String affectedParticipants = null; try { while (recoveryRulesReader.hasNext() && !(recoveryRulesReader.isEndElement() && recoveryRulesReader.getLocalName().equals(Constants.THROW_EXCEPTION))) { recoveryRulesReader.next(); // if (recoveryRulesReader.isStartElement() && recoveryRulesReader.getLocalName().equals(Constants.AFFECTED_PARTICIPANTS)) { affectedParticipants = recoveryRulesReader.getElementText(); } } } catch (XMLStreamException ex) { Logger.getLogger(GuardianGroupImpl.class.getName()).log(Level.SEVERE, null, ex); } return affectedParticipants; } 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; String exceptionLevel = null; GlobalExceptionInterface resolvedEx = null; while (resolutionTreesElements.hasNext()) { tree = (OMElement) resolutionTreesElements.next(); exceptionLevel = tree.getAttributeValue(Constants.EXCEPTION_LEVEL_QNAME); resolvedEx = checkExceptionResolutionTree(exceptionList, tree.getChildElements(), new WrapperInteger(exceptionList.size())); 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. //FIXME: Check for equal exception classes on the tree //FIXME: Implement the Lowest common ancestor algorithm private GlobalExceptionInterface checkExceptionResolutionTree(List exceptionList, Iterator elements, WrapperInteger currentListSize) { OMElement el; OMAttribute at; String exClass = null; GlobalExceptionInterface resolvedEx = null; boolean isLeaf = false; while (elements.hasNext()) { el = (OMElement) elements.next(); // if (el.getLocalName().equals(Constants.EXCEPTION)) { Iterator it = el.getAllAttributes(); while (it.hasNext()) { at = (OMAttribute) it.next(); if (at.getLocalName().equals(Constants.CLASS)) { exClass = at.getAttributeValue(); } } try { for (GlobalExceptionInterface ex : exceptionList) { if (Class.forName(exClass).equals(ex.getClass())) { currentListSize.decrement(); break; } } } catch (ClassNotFoundException ex1) { Logger.getLogger(GuardianGroupImpl.class.getName()).log(Level.SEVERE, null, ex1); } Iterator children = el.getChildElements(); if (children.hasNext()) { resolvedEx = checkExceptionResolutionTree(exceptionList, children, currentListSize); } else { isLeaf = true; } } } if (resolvedEx == null && currentListSize.getValue() == 0 && !isLeaf) { Class exceptionClass; try { exceptionClass = Class.forName(exClass); 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; } private List getMatchingParticipants(String regularExpression, GlobalExceptionInterface signaledException) { List matchingParticipants = new LinkedList(); String[] splitByComma = regularExpression.split(","); //Invalid regular expression if (splitByComma.length > 2) { throw new InvalidRegularExpression("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); } /*//All concurrent signaler were found if (matchingParticipants.size() == signaledException.getSignalingParticipants().size()) { break; }*/ } } else if (regularExpression.toUpperCase().equals("!SIGNALER")) { for (GuardianMember gm : guardianList) { if (!signaledException.getSignalingParticipants().contains(gm.getParticipantIdentifier())) { //if (!gm.getParticipantIdentifier().equals(signaledException.getSignalingParticipant())) { 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 InvalidRegularExpression("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); } /*//All concurrent signaler were found if (matchingParticipants.size() == signaledException.getSignalingParticipants().size()) { break; }*/ // //ConcurrentGlobalException: there are more than one signaler // if (signaledException instanceof ConcurrentGlobalException) { // List signalingPartcipants = ((ConcurrentGlobalException) signaledException).getSignalingParticipants(); // // if (signalingPartcipants.contains(gm.getParticipantIdentifier())) { // matchingParticipants.add(gm); // count++; // } // // //All concurrent signaler were found // if (count == signalingPartcipants.size()) { // break; // } // // } else if (gm.getParticipantIdentifier().equals(signaledException.getSignalingParticipant())) { // matchingParticipants.add(gm); // break; // } } //Test if the participant is not Signaler else { if (!signaledException.getSignalingParticipants().contains(gm.getParticipantIdentifier())) { matchingParticipants.add(gm); } // //ConcurrentGlobalException: there are more than one signaler // if (signaledException instanceof ConcurrentGlobalException) { // List signalingPartcipants = ((ConcurrentGlobalException) signaledException).getSignalingParticipants(); // // if (!(signalingPartcipants.contains(gm.getParticipantIdentifier()))) { // matchingParticipants.add(gm); // } // // } else if (!gm.getParticipantIdentifier().equals(signaledException.getSignalingParticipant())) { // matchingParticipants.add(gm); // } } } } } return matchingParticipants; } // private List getMatchingParticipantsOriginal(String regularExpression, GlobalExceptionInterface signaledException) { // List matchingParticipants = new LinkedList(); // // if (regularExpression.toUpperCase().equals("SIGNALER")) { // for (GuardianMember gm : guardianList) { // if (gm.getParticipantIdentifier().equals(signaledException.getSignalingParticipant())) { // matchingParticipants.add(gm); // break; // } // } // } else if (regularExpression.toUpperCase().equals("!SIGNALER")) { // for (GuardianMember gm : guardianList) { // if (!gm.getParticipantIdentifier().equals(signaledException.getSignalingParticipant())) { // 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); // } // } // } // // return matchingParticipants; // } /* Valid expressions: *, .*, , *., *..*, * *..*..*, || * * Invalid expressions: *.*, **, * * Not supported yet: !, ! || , !( || ) */ private String createJavaRegularExpression(String regularExpression) throws InvalidRegularExpression { 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 InvalidRegularExpression(); } //* 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(); } } private class WrapperInteger { private int value; public WrapperInteger() { } public WrapperInteger(int value) { this.value = value; } public void setValue(int value) { this.value = value; } public int getValue() { return this.value; } public int increment() { return ++value; } public int increment(int amount) { return value += amount; } public int decrement() { return --value; } public int decrement(int amount) { return value -= value; } @Override public String toString() { return Integer.toString(value); } } }