View Javadoc

1   /**
2    * This file Copyright (c) 2003-2014 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.module.exchangesimple;
35  
36  import info.magnolia.cms.core.ItemType;
37  import info.magnolia.cms.exchange.ActivationManagerFactory;
38  import info.magnolia.cms.exchange.ExchangeException;
39  import info.magnolia.cms.exchange.Subscriber;
40  import info.magnolia.cms.exchange.Subscription;
41  import info.magnolia.cms.security.SecurityUtil;
42  
43  import java.io.IOException;
44  import java.net.MalformedURLException;
45  import java.net.URLConnection;
46  import java.util.ArrayList;
47  import java.util.Collection;
48  import java.util.Iterator;
49  import java.util.List;
50  
51  import org.apache.commons.lang.StringUtils;
52  import org.slf4j.Logger;
53  import org.slf4j.LoggerFactory;
54  
55  import EDU.oswego.cs.dl.util.concurrent.CountDown;
56  import EDU.oswego.cs.dl.util.concurrent.Sync;
57  
58  /**
59   * Implementation of syndicator that simply sends all activated content over http connection specified in the subscriber.
60   * 
61   * @author Sameer Charles $Id$
62   */
63  public class SimpleSyndicator extends BaseSyndicatorImpl {
64      private static final Logger log = LoggerFactory.getLogger(SimpleSyndicator.class);
65  
66      @Override
67      public void activate(final ActivationContent activationContent, String nodePath) throws ExchangeException {
68          List<Exception> errors = new ArrayList<Exception>();
69          try {
70              String nodeUUID = activationContent.getproperty(NODE_UUID);
71              final ExchangeTask task;
72              // Create runnable task for subscriber execute
73              if (Boolean.parseBoolean(activationContent.getproperty(ItemType.DELETED_NODE_MIXIN))) {
74                  task = getDeactivateTask(nodeUUID, nodePath);
75              } else {
76                  task = getActivateTask(activationContent, nodePath);
77              }
78              errors.addAll(executeExchangeTask(task));
79  
80              // clean storage BEFORE re-throwing exception
81              executeInPool(new Runnable() {
82                  @Override
83                  public void run() {
84                      cleanTemporaryStore(activationContent);
85                  }
86              });
87          } finally {
88              activationContent.removeTempFile();
89          }
90          handleErrors(errors);
91          long end = System.currentTimeMillis();
92      }
93  
94      protected void handleErrors(List<Exception> errors) throws ExchangeException {
95          // collect all the errors and send them back.
96          if (!errors.isEmpty()) {
97              Exception e = errors.get(0);
98              log.error(e.getMessage(), e);
99              throw new ExchangeException("1 error detected: \n" + e.getMessage(), e);
100         }
101     }
102 
103     private ExchangeTask getActivateTask(final ActivationContent activationContent, final String nodePath) {
104         ExchangeTask r = new ExchangeTask() {
105             @Override
106             public void runTask(Subscriber subscriber) throws ExchangeException {
107                 activate(subscriber, activationContent, nodePath);
108             }
109         };
110         return r;
111     }
112 
113     @Override
114     public void doDeactivate(String nodeUUID, String nodePath) throws ExchangeException {
115         List<Exception> errors = executeExchangeTask(getDeactivateTask(nodeUUID, nodePath));
116         handleErrors(errors);
117     }
118 
119     private List<Exception> executeExchangeTask(ExchangeTask runnable) throws ExchangeException {
120         Collection<Subscriber> allSubscribers = getSubscribers();
121         Iterator<Subscriber> subscriberIterator = allSubscribers.iterator();
122         final Sync done = new CountDown(allSubscribers.size());
123         final List<Exception> errors = new ArrayList<Exception>();
124         int count = 0;
125         while (subscriberIterator.hasNext()) {
126             count++;
127             final Subscriber subscriber = subscriberIterator.next();
128             if (subscriber.isActive()) {
129                 // TODO: Inject?
130                 runnable.setErrors(errors);
131                 runnable.setSubscriber(subscriber);
132                 runnable.setSync(done);
133                 // Create runnable task for each subscriber.
134                 executeInPool(runnable);
135                 break;
136             } else {
137                 done.release();
138             }
139         } // end of subscriber loop
140 
141         // release unused barriers
142         for (; count < allSubscribers.size(); count++) {
143             done.release();
144         }
145 
146         // wait until all tasks are executed before returning back to user to make sure errors can be propagated back to the user.
147         acquireIgnoringInterruption(done);
148 
149         return errors;
150     }
151 
152     private ExchangeTask getDeactivateTask(final String nodeUUID, final String nodePath) {
153         ExchangeTask r = new ExchangeTask() {
154             @Override
155             public void runTask(Subscriber subscriber) throws ExchangeException {
156                 doDeactivate(subscriber, nodeUUID, nodePath);
157             }
158         };
159         return r;
160     }
161 
162     /**
163      * Deactivate from a specified subscriber.
164      * 
165      * @param subscriber
166      * @throws ExchangeException
167      */
168     @Override
169     public String doDeactivate(Subscriber subscriber, String nodeUUID, String path) throws ExchangeException {
170         Subscription subscription = subscriber.getMatchedSubscription(path, this.repositoryName);
171         boolean success = true;
172         if (null != subscription) {
173             String urlString = getDeactivationURL(subscriber);
174             URLConnection urlConnection = null;
175             try {
176                 urlConnection = prepareConnection(subscriber, urlString);
177 
178                 this.addDeactivationHeaders(urlConnection, nodeUUID, null);
179                 String status = urlConnection.getHeaderField(ACTIVATION_ATTRIBUTE_STATUS);
180 
181                 if (StringUtils.equals(status, ACTIVATION_HANDSHAKE)) {
182                     String handshakeKey = urlConnection.getHeaderField(ACTIVATION_AUTH);
183                     // receive all pending data
184                     urlConnection.getContent();
185                     releaseConnection(urlConnection);
186 
187                     // transport the data again
188                     urlConnection = prepareConnection(subscriber, getActivationURL(subscriber));
189                     // and get the version & status again
190                     this.addDeactivationHeaders(urlConnection, nodeUUID, handshakeKey);
191                     status = urlConnection.getHeaderField(ACTIVATION_ATTRIBUTE_STATUS);
192                 }
193 
194                 // check if the activation failed
195                 if (StringUtils.equals(status, ACTIVATION_FAILED)) {
196                     String message = urlConnection.getHeaderField(ACTIVATION_ATTRIBUTE_MESSAGE);
197                     throw new ExchangeException("Message received from subscriber: " + message);
198                 }
199 
200                 urlConnection.getContent();
201 
202             } catch (MalformedURLException e) {
203                 success = false;
204                 activationMonitor.logError(path, user.getName(), workspaceName, subscriber.getName(), e, true);
205                 throw new ExchangeException("Incorrect URL for subscriber " + subscriber + "[" + SecurityUtil.stripPasswordFromUrl(urlString) + "]");
206             } catch (IOException e) {
207                 success = false;
208                 activationMonitor.logError(path, user.getName(), workspaceName, subscriber.getName(), e, true);
209                 throw new ExchangeException("Not able to send the deactivation request [" + SecurityUtil.stripPasswordFromUrl(urlString) + "]: " + e.getMessage());
210             } catch (Exception e) {
211                 success = false;
212                 activationMonitor.logError(path, user.getName(), workspaceName, subscriber.getName(), e, true);
213                 throw new ExchangeException(e);
214             } finally {
215                 releaseConnection(urlConnection);
216                 activationMonitor.logActivation(path, user.getName(), workspaceName, subscriber.getName(), true, success);
217             }
218         }
219         return null;
220     }
221 
222     protected Collection<Subscriber> getSubscribers() {
223         return ActivationManagerFactory.getActivationManager().getSubscribers();
224     }
225 }