Coverage Report - org.jscsi.target.Configuration
 
Classes in this File Line Coverage Branch Coverage Complexity
Configuration
0%
0/82
0%
0/30
1.846
 
 1  
 package org.jscsi.target;
 2  
 
 3  
 
 4  
 import java.io.File;
 5  
 import java.io.IOException;
 6  
 import java.net.InetAddress;
 7  
 import java.util.ArrayList;
 8  
 import java.util.List;
 9  
 
 10  
 import javax.xml.XMLConstants;
 11  
 import javax.xml.parsers.DocumentBuilder;
 12  
 import javax.xml.parsers.DocumentBuilderFactory;
 13  
 import javax.xml.parsers.ParserConfigurationException;
 14  
 import javax.xml.transform.dom.DOMResult;
 15  
 import javax.xml.transform.dom.DOMSource;
 16  
 import javax.xml.validation.Schema;
 17  
 import javax.xml.validation.SchemaFactory;
 18  
 import javax.xml.validation.Validator;
 19  
 
 20  
 import org.jscsi.target.scsi.lun.LogicalUnitNumber;
 21  
 import org.jscsi.target.settings.TextKeyword;
 22  
 import org.jscsi.target.storage.IStorageModule;
 23  
 import org.jscsi.target.storage.JCloudsStorageModule;
 24  
 import org.jscsi.target.storage.RandomAccessStorageModule;
 25  
 import org.jscsi.target.storage.SynchronizedRandomAccessStorageModule;
 26  
 import org.w3c.dom.Document;
 27  
 import org.w3c.dom.Element;
 28  
 import org.w3c.dom.Node;
 29  
 import org.w3c.dom.NodeList;
 30  
 import org.w3c.dom.Text;
 31  
 import org.xml.sax.SAXException;
 32  
 
 33  
 
 34  
 /**
 35  
  * Instances of {@link Configuration} provides access target-wide parameters, variables that are the same across all
 36  
  * sessions and connections that do not change after initialization and which play a role during text parameter
 37  
  * negotiation. Some of these parameters are provided or can be overridden by the content of an XML file -
 38  
  * <code>jscsi-target.xml</code>.
 39  
  * 
 40  
  * @author Andreas Ergenzinger, University of Konstanz
 41  
  */
 42  
 public class Configuration {
 43  
 
 44  
     public static final String ELEMENT_TARGET_LIST = "TargetList"; // Name of
 45  
                                                                    // node that
 46  
                                                                    // contains
 47  
                                                                    // list of
 48  
     // targets
 49  
 
 50  
     public static final String ELEMENT_TARGET = "Target"; // Name for nodes
 51  
                                                           // that contain a
 52  
                                                           // target
 53  
     // Target configuration elements
 54  
     public static final String ELEMENT_SYNCFILESTORAGE = "SyncFileStorage";
 55  
     public static final String ELEMENT_ASYNCFILESTORAGE = "AsyncFileStorage";
 56  
     public static final String ELEMENT_JCLOUDSSTORAGE = "JCloudsStorage";
 57  
     public static final String ELEMENT_FILESTORAGE = "FileStorage";
 58  
     public static final String ELEMENT_CREATE = "Create";
 59  
     public static final String ATTRIBUTE_SIZE = "size";
 60  
 
 61  
     // Global configuration elements
 62  
     public static final String ELEMENT_ALLOWSLOPPYNEGOTIATION = "AllowSloppyNegotiation";
 63  
     public static final String ELEMENT_PORT = "Port";
 64  
 
 65  
     // --------------------------------------------------------------------------
 66  
     // --------------------------------------------------------------------------
 67  
 
 68  
     /**
 69  
      * The relative path (to the project) of the main directory of all configuration files.
 70  
      */
 71  0
     private static final File CONFIG_DIR = new File(new StringBuilder("src").append(File.separator).append("main").append(File.separator).append("resources").append(File.separator).toString());
 72  
 
 73  
     /**
 74  
      * The file name of the XML Schema configuration file for the global settings.
 75  
      */
 76  0
     public static final File CONFIGURATION_SCHEMA_FILE = new File(CONFIG_DIR, "jscsi-target.xsd");
 77  
 
 78  
     /** The file name, which contains all global settings. */
 79  0
     public static final File CONFIGURATION_CONFIG_FILE = new File(CONFIG_DIR, "jscsi-target.xml");
 80  
 
 81  
     // --------------------------------------------------------------------------
 82  
     // --------------------------------------------------------------------------
 83  
 
 84  
     protected final List<Target> targets;
 85  
 
 86  
     /**
 87  
      * The <code>TargetAddress</code> parameter (the jSCSI Target's IP address).
 88  
      * <p>
 89  
      * This parameter is initialized automatically.
 90  
      */
 91  
     protected String targetAddress;
 92  
 
 93  
     /**
 94  
      * The port used by the jSCSI Target for listening for new connections.
 95  
      * <p>
 96  
      * The default port number is 3260. This value may be overridden by specifying a different value in the
 97  
      * configuration file.
 98  
      */
 99  
     protected int port;
 100  
 
 101  
     /**
 102  
      * This variable toggles the strictness with which the parameters <code>IFMarkInt</code> and <code>OFMarkInt</code>
 103  
      * are processed, when provided by the initiator. Usually the offered values must have to following format:
 104  
      * <code>smallInteger~largeInteger</code>, however the jSCSI Initiator sends only single integers as <i>value</i>
 105  
      * part of the <i>key-value</i> pairs. Since the value of these two parameters always are <code>Irrelevant</code>,
 106  
      * this bug can be ignored without any negative consequences by setting {@link #allowSloppyNegotiation} to
 107  
      * <code>true</code> in the configuration file. The default is <code>false</code>.
 108  
      */
 109  
     protected boolean allowSloppyNegotiation;// TODO fix in jSCSI Initiator and
 110  
                                              // remove
 111  
 
 112  
     /**
 113  
      * The <code>TargetPortalGroupTag</code> parameter.
 114  
      */
 115  0
     protected final int targetPortalGroupTag = 1;
 116  
 
 117  
     /**
 118  
      * The Logical Unit Number of the virtual Logical Unit.
 119  
      */
 120  0
     protected final LogicalUnitNumber logicalUnitNumber = new LogicalUnitNumber(0L);
 121  
 
 122  
     /**
 123  
      * The <code>MaxRecvDataSegmentLength</code> parameter for PDUs sent in the out direction (i.e. initiator to
 124  
      * target).
 125  
      * <p>
 126  
      * Since the value of this variable is equal to the specified default value, it does not have to be declared during
 127  
      * login.
 128  
      */
 129  0
     protected final int outMaxRecvDataSegmentLength = 8192;
 130  
 
 131  
     /**
 132  
      * The maximum number of consecutive Login PDUs or Text Negotiation PDUs the target will accept in a single
 133  
      * sequence.
 134  
      * <p>
 135  
      * The iSCSI standard does not dictate a minimum or maximum text PDU sequence length, but only suggests to select a
 136  
      * value large enough for all expected key-value pairs that might be sent in a single sequence. A limit should be
 137  
      * imposed, however, to prevent {@link OutOfMemoryError}s resulting from malicious or accidental text PDU sequences
 138  
      * of extreme lengths.
 139  
      * <p>
 140  
      * Since all common text parameters (plus values) easily fit into a single text PDU with the default data segment
 141  
      * size, this value could be set to <code>1</code> without negatively affecting compatibility with most initiators.
 142  
      */
 143  0
     private final int maxRecvTextPduSequenceLength = 4;
 144  
 
 145  0
     public Configuration (final String pTargetAddress) throws IOException {
 146  0
         port = 3260;
 147  
 
 148  0
         if (pTargetAddress.equals("")) {
 149  0
             targetAddress = InetAddress.getLocalHost().getHostAddress();
 150  
 
 151  
         } else {
 152  0
             targetAddress = pTargetAddress;
 153  
         }
 154  
 
 155  0
         targets = new ArrayList<Target>();
 156  0
     }
 157  
 
 158  
     public int getInMaxRecvTextPduSequenceLength () {
 159  0
         return maxRecvTextPduSequenceLength;
 160  
     }
 161  
 
 162  
     public int getOutMaxRecvDataSegmentLength () {
 163  0
         return outMaxRecvDataSegmentLength;
 164  
     }
 165  
 
 166  
     public String getTargetAddress () {
 167  0
         return targetAddress;
 168  
     }
 169  
 
 170  
     public int getPort () {
 171  0
         return port;
 172  
     }
 173  
 
 174  
     public boolean getAllowSloppyNegotiation () {
 175  0
         return allowSloppyNegotiation;
 176  
     }
 177  
 
 178  
     public int getTargetPortalGroupTag () {
 179  0
         return targetPortalGroupTag;
 180  
     }
 181  
 
 182  
     public LogicalUnitNumber getLogicalUnitNumber () {
 183  0
         return logicalUnitNumber;
 184  
     }
 185  
 
 186  
     public List<Target> getTargets () {
 187  0
         return targets;
 188  
     }
 189  
 
 190  
     public static Configuration create (final String pTargetAddress) throws SAXException , ParserConfigurationException , IOException {
 191  0
         return create(CONFIGURATION_SCHEMA_FILE, CONFIGURATION_CONFIG_FILE, pTargetAddress);
 192  
     }
 193  
 
 194  
     /**
 195  
      * Reads the given configuration file in memory and creates a DOM representation.
 196  
      * 
 197  
      * @throws SAXException If this operation is supported but failed for some reason.
 198  
      * @throws ParserConfigurationException If a {@link DocumentBuilder} cannot be created which satisfies the
 199  
      *             configuration requested.
 200  
      * @throws IOException If any IO errors occur.
 201  
      */
 202  
     public static Configuration create (final File schemaLocation, final File configFile, final String pTargetAddress) throws SAXException , ParserConfigurationException , IOException {
 203  0
         final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
 204  0
         final Schema schema = schemaFactory.newSchema(schemaLocation);
 205  
 
 206  
         // create a validator for the document
 207  0
         final Validator validator = schema.newValidator();
 208  
 
 209  0
         final DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
 210  0
         domFactory.setNamespaceAware(true); // never forget this
 211  0
         final DocumentBuilder builder = domFactory.newDocumentBuilder();
 212  0
         final Document doc = builder.parse(configFile);
 213  
 
 214  0
         final DOMSource source = new DOMSource(doc);
 215  0
         final DOMResult result = new DOMResult();
 216  
 
 217  0
         validator.validate(source, result);
 218  0
         Document root = (Document) result.getNode();
 219  
 
 220  
         // TargetName
 221  0
         Configuration returnConfiguration = new Configuration(pTargetAddress);
 222  0
         Element targetListNode = (Element) root.getElementsByTagName(ELEMENT_TARGET_LIST).item(0);
 223  0
         NodeList targetList = targetListNode.getElementsByTagName(ELEMENT_TARGET);
 224  0
         for (int curTargetNum = 0; curTargetNum < targetList.getLength(); curTargetNum++) {
 225  0
             Target curTargetInfo = parseTargetElement((Element) targetList.item(curTargetNum));
 226  0
             synchronized (returnConfiguration.targets) {
 227  0
                 returnConfiguration.targets.add(curTargetInfo);
 228  0
             }
 229  
 
 230  
         }
 231  
 
 232  
         // else it is null
 233  
 
 234  
         // port
 235  0
         if (root.getElementsByTagName(ELEMENT_PORT).getLength() > 0)
 236  0
             returnConfiguration.port = Integer.parseInt(root.getElementsByTagName(ELEMENT_PORT).item(0).getTextContent());
 237  
         else
 238  0
             returnConfiguration.port = 3260;
 239  
 
 240  
         // support sloppy text parameter negotiation (i.e. the jSCSI Initiator)?
 241  0
         final Node allowSloppyNegotiationNode = root.getElementsByTagName(ELEMENT_ALLOWSLOPPYNEGOTIATION).item(0);
 242  0
         if (allowSloppyNegotiationNode == null)
 243  0
             returnConfiguration.allowSloppyNegotiation = false;
 244  
         else
 245  0
             returnConfiguration.allowSloppyNegotiation = Boolean.parseBoolean(allowSloppyNegotiationNode.getTextContent());
 246  
 
 247  0
         return returnConfiguration;
 248  
 
 249  
     }
 250  
 
 251  
     protected static Target parseTargetElement (Element targetElement) throws IOException {
 252  
         // TargetName
 253  
         // TargetName
 254  0
         Node nextNode = chopWhiteSpaces(targetElement.getFirstChild());
 255  
         // assert
 256  
         // nextNode.getLocalName().equals(OperationalTextKey.TARGET_NAME);
 257  0
         String targetName = nextNode.getTextContent();
 258  
 
 259  
         // TargetAlias (optional)
 260  0
         nextNode = chopWhiteSpaces(nextNode.getNextSibling());
 261  0
         String targetAlias = "";
 262  0
         if (nextNode.getLocalName().equals(TextKeyword.TARGET_ALIAS)) {
 263  0
             targetAlias = nextNode.getTextContent();
 264  0
             nextNode = chopWhiteSpaces(nextNode.getNextSibling());
 265  
         }
 266  
 
 267  
         // Finding out the concrete storage
 268  0
         Class<? extends IStorageModule> kind = null;
 269  0
         switch (nextNode.getLocalName()) {
 270  
             case ELEMENT_SYNCFILESTORAGE :
 271  0
                 kind = SynchronizedRandomAccessStorageModule.class;
 272  0
                 break;
 273  
             case ELEMENT_ASYNCFILESTORAGE :
 274  0
                 kind = RandomAccessStorageModule.class;
 275  0
                 break;
 276  
             case ELEMENT_JCLOUDSSTORAGE :
 277  0
                 kind = JCloudsStorageModule.class;
 278  
                 break;
 279  
         }
 280  
 
 281  
         // Getting storagepath
 282  0
         nextNode = nextNode.getFirstChild();
 283  0
         nextNode = chopWhiteSpaces(nextNode);
 284  
         // assert nextNode.getLocalName().equals(ELEMENT_PATH);
 285  0
         String storageFilePath = nextNode.getTextContent();
 286  
 
 287  
         // CreateNode with size
 288  0
         nextNode = chopWhiteSpaces(nextNode.getNextSibling());
 289  0
         long storageLength = -1;
 290  0
         boolean create = true;
 291  0
         if (nextNode.getLocalName().equals(ELEMENT_CREATE)) {
 292  0
             Node sizeAttribute = nextNode.getAttributes().getNamedItem(ATTRIBUTE_SIZE);
 293  0
             storageLength = Math.round(((Double.valueOf(sizeAttribute.getTextContent())) * Math.pow(1024, 3)));
 294  0
         } else {
 295  0
             storageLength = new File(storageFilePath).length();
 296  0
             create = false;
 297  
             // assert nextNode.getLocalName().equals(ELEMENT_DONTCREATE);
 298  
         }
 299  0
         final IStorageModule module = RandomAccessStorageModule.open(new File(storageFilePath), storageLength, create, kind);
 300  
 
 301  0
         return new Target(targetName, targetAlias, module);
 302  
 
 303  
     }
 304  
 
 305  
     protected static Node chopWhiteSpaces (final Node node) {
 306  0
         Node toIterate = node;
 307  0
         while (toIterate instanceof Text && toIterate.getTextContent().trim().length() == 0) {
 308  0
             toIterate = toIterate.getNextSibling();
 309  
         }
 310  0
         return toIterate;
 311  
     }
 312  
 
 313  
 }