| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
| MkStorage | 
  | 
  | 1.1764705882352942;1.176 | ||||
| MkStorage$InFile | 
  | 
  | 1.1764705882352942;1.176 | ||||
| MkStorage$InFile$AjcClosure1 | 
  | 
  | 1.1764705882352942;1.176 | ||||
| MkStorage$InFile$AjcClosure3 | 
  | 
  | 1.1764705882352942;1.176 | ||||
| MkStorage$InFile$AjcClosure5 | 
  | 
  | 1.1764705882352942;1.176 | ||||
| MkStorage$InFile$AjcClosure7 | 
  | 
  | 1.1764705882352942;1.176 | ||||
| MkStorage$Synced | 
  | 
  | 1.1764705882352942;1.176 | ||||
| MkStorage$Synced$AjcClosure1 | 
  | 
  | 1.1764705882352942;1.176 | ||||
| MkStorage$Synced$AjcClosure3 | 
  | 
  | 1.1764705882352942;1.176 | ||||
| MkStorage$Synced$AjcClosure5 | 
  | 
  | 1.1764705882352942;1.176 | ||||
| MkStorage$Synced$AjcClosure7 | 
  | 
  | 1.1764705882352942;1.176 | 
| 1 | 626 |  /** | 
| 2 |   * Copyright (c) 2013-2017, jcabi.com | |
| 3 |   * All rights reserved. | |
| 4 |   * | |
| 5 |   * Redistribution and use in source and binary forms, with or without | |
| 6 |   * modification, are permitted provided that the following conditions | |
| 7 |   * are met: 1) Redistributions of source code must retain the above | |
| 8 |   * copyright notice, this list of conditions and the following | |
| 9 |   * disclaimer. 2) Redistributions in binary form must reproduce the above | |
| 10 |   * copyright notice, this list of conditions and the following | |
| 11 |   * disclaimer in the documentation and/or other materials provided | |
| 12 |   * with the distribution. 3) Neither the name of the jcabi.com nor | |
| 13 |   * the names of its contributors may be used to endorse or promote | |
| 14 |   * products derived from this software without specific prior written | |
| 15 |   * permission. | |
| 16 |   * | |
| 17 |   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 18 |   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT | |
| 19 |   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND | |
| 20 |   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL | |
| 21 |   * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, | |
| 22 |   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
| 23 |   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
| 24 |   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 25 |   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |
| 26 |   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
| 27 |   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED | |
| 28 |   * OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 29 |   */ | |
| 30 |  package com.jcabi.github.mock; | |
| 31 | ||
| 32 |  import com.jcabi.aspects.Immutable; | |
| 33 |  import com.jcabi.aspects.Loggable; | |
| 34 |  import com.jcabi.xml.XML; | |
| 35 |  import com.jcabi.xml.XMLDocument; | |
| 36 |  import java.io.File; | |
| 37 |  import java.io.IOException; | |
| 38 |  import java.nio.charset.StandardCharsets; | |
| 39 |  import lombok.EqualsAndHashCode; | |
| 40 |  import org.apache.commons.io.FileUtils; | |
| 41 |  import org.xembly.Directive; | |
| 42 |  import org.xembly.Xembler; | |
| 43 | ||
| 44 |  /** | |
| 45 |   * Storage of Github data. | |
| 46 |   * | |
| 47 |   * @author Yegor Bugayenko (yegor@tpc2.com) | |
| 48 |   * @version $Id: 9c598ee290a4c1460f6659490c0aacce737ce483 $ | |
| 49 |   * @since 0.5 | |
| 50 |   * @checkstyle MultipleStringLiteralsCheck (200 lines) | |
| 51 |   */ | |
| 52 | @Immutable  | |
| 53 |  @SuppressWarnings("PMD.TooManyMethods") | |
| 54 | public interface MkStorage {  | |
| 55 | ||
| 56 |      /** | |
| 57 |       * Get full XML. | |
| 58 |       * @return XML | |
| 59 |       * @throws IOException If there is any I/O problem, or if the current | |
| 60 |       *  storage is locked by another thread. | |
| 61 |       */ | |
| 62 |      XML xml() throws IOException; | |
| 63 | ||
| 64 |      /** | |
| 65 |       * Update XML with this directives. | |
| 66 |       * @param dirs Directives | |
| 67 |       * @throws IOException If there is any I/O problem, or if the current | |
| 68 |       *  storage is locked by another thread. | |
| 69 |       */ | |
| 70 |      void apply( | |
| 71 | Iterable<Directive> dirs  | |
| 72 |      ) throws IOException; | |
| 73 | ||
| 74 |      /** | |
| 75 |       * Locks storage to the current thread. | |
| 76 |       * | |
| 77 |       * <p>If the lock is available, grant it | |
| 78 |       * to the calling thread and block all operations from other threads. | |
| 79 |       * If not available, wait for the holder of the lock to release it with | |
| 80 |       * {@link #unlock()} before any other operations can be performed. | |
| 81 |       * | |
| 82 |       * <p>Locking behavior is reentrant, which means a thread can invoke | |
| 83 |       * {@link #lock()} multiple times, where a hold count is maintained. | |
| 84 |       * @throws IOException If there is any I/O problem | |
| 85 |       */ | |
| 86 | void lock() throws IOException;  | |
| 87 | ||
| 88 |      /** | |
| 89 |       * Unlock storage. | |
| 90 |       * | |
| 91 |       * <p>Locking behavior is reentrant, thus if the thread invoked | |
| 92 |       * {@link #lock()} multiple times, the hold count is decremented. If the | |
| 93 |       * hold count reaches 0, the lock is released. | |
| 94 |       * | |
| 95 |       * <p>If the current thread does not hold the lock, an | |
| 96 |       * {@link IllegalMonitorStateException} will be thrown. | |
| 97 |       * @throws IOException If there is any I/O problem | |
| 98 |       */ | |
| 99 | void unlock() throws IOException;  | |
| 100 | ||
| 101 |      /** | |
| 102 |       * In file. | |
| 103 |       */ | |
| 104 | @Immutable  | |
| 105 | 8 |      @EqualsAndHashCode(of = "name") | 
| 106 | @Loggable(Loggable.DEBUG)  | |
| 107 | final class InFile implements MkStorage {  | |
| 108 |          /** | |
| 109 |           * File name. | |
| 110 |           */ | |
| 111 | private final transient String name;  | |
| 112 |          /** | |
| 113 |           * Public ctor. | |
| 114 |           * @throws IOException If there is any I/O problem | |
| 115 |           */ | |
| 116 | public InFile() throws IOException {  | |
| 117 | 351 | this(File.createTempFile("jcabi-github", ".xml"));  | 
| 118 | 348 | new File(this.name).deleteOnExit();  | 
| 119 | 353 | }  | 
| 120 |          /** | |
| 121 |           * Public ctor. | |
| 122 |           * @param file File to use | |
| 123 |           * @throws IOException If there is any I/O problem | |
| 124 |           */ | |
| 125 |          public InFile( | |
| 126 |              final File file | |
| 127 | 353 |          ) throws IOException { | 
| 128 | 353 |              FileUtils.write(file, "<github/>", StandardCharsets.UTF_8); | 
| 129 | 348 |              this.name = file.getAbsolutePath(); | 
| 130 | 348 | }  | 
| 131 | @Override  | |
| 132 |          public String toString() { | |
| 133 |              try { | |
| 134 | 3 | return this.xml().toString();  | 
| 135 | 0 |              } catch (final IOException ex) { | 
| 136 | 0 |                  throw new IllegalStateException(ex); | 
| 137 | }  | |
| 138 | }  | |
| 139 | @Override  | |
| 140 | public XML xml() throws IOException {  | |
| 141 | 5372 | synchronized (this.name) {  | 
| 142 | 5368 | return new XMLDocument(  | 
| 143 | 2683 | FileUtils.readFileToString(  | 
| 144 | new File(this.name), StandardCharsets.UTF_8  | |
| 145 | )  | |
| 146 | );  | |
| 147 | 0 |              } | 
| 148 | }  | |
| 149 | @Override  | |
| 150 | public void apply(  | |
| 151 |              final Iterable<Directive> dirs | |
| 152 |          ) throws IOException { | |
| 153 | 3653 | synchronized (this.name) {  | 
| 154 | 3667 | FileUtils.write(  | 
| 155 | new File(this.name),  | |
| 156 |                      new XMLDocument( | |
| 157 | 1831 | new Xembler(dirs).applyQuietly(this.xml().node())  | 
| 158 | 1835 | ).toString(),  | 
| 159 | StandardCharsets.UTF_8  | |
| 160 | );  | |
| 161 | 1825 | }  | 
| 162 | 1832 | }  | 
| 163 | @Override  | |
| 164 | public void lock() {  | |
| 165 |              // nothing | |
| 166 | 26 | }  | 
| 167 | @Override  | |
| 168 | public void unlock() {  | |
| 169 |              // nothing | |
| 170 | 26 | }  | 
| 171 | }  | |
| 172 | ||
| 173 |      /** | |
| 174 |       * Syncronized. | |
| 175 |       */ | |
| 176 | @Immutable  | |
| 177 | 23 |      @EqualsAndHashCode(of = { "origin", "lock" }) | 
| 178 | @Loggable(Loggable.DEBUG)  | |
| 179 | final class Synced implements MkStorage {  | |
| 180 |          /** | |
| 181 |           * Original storage. | |
| 182 |           */ | |
| 183 | private final transient MkStorage origin;  | |
| 184 |          /** | |
| 185 |           * Lock object. | |
| 186 |           */ | |
| 187 | 286 | private final transient ImmutableReentrantLock lock =  | 
| 188 |              new ImmutableReentrantLock(); | |
| 189 |          /** | |
| 190 |           * Public ctor. | |
| 191 |           * @param storage Original | |
| 192 |           */ | |
| 193 | 286 | public Synced(final MkStorage storage) {  | 
| 194 | 286 |              this.origin = storage; | 
| 195 | 286 | }  | 
| 196 | @Override  | |
| 197 |          public String toString() { | |
| 198 | 3 | return this.origin.toString();  | 
| 199 | }  | |
| 200 | @Override  | |
| 201 | public XML xml() throws IOException {  | |
| 202 | 1583 | return this.origin.xml();  | 
| 203 | }  | |
| 204 | @Override  | |
| 205 | public void apply(final Iterable<Directive> dirs) throws IOException {  | |
| 206 | 3363 |              this.origin.apply(dirs); | 
| 207 | 1679 | }  | 
| 208 | @Override  | |
| 209 | public void lock() {  | |
| 210 | 625 |              this.lock.lock(); | 
| 211 | 314 | }  | 
| 212 | @Override  | |
| 213 | public void unlock() {  | |
| 214 | 623 |              this.lock.unlock(); | 
| 215 | 311 | }  | 
| 216 | }  | |
| 217 | ||
| 218 | }  |