/*
* JBoss, Home of Professional Open Source.
* Copyright 2013, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.as.patching.metadata;
import static org.jboss.as.controller.parsing.ParseUtils.requireNoContent;
import static org.jboss.as.controller.parsing.ParseUtils.unexpectedAttribute;
import static org.jboss.as.controller.parsing.ParseUtils.unexpectedElement;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import org.jboss.as.patching.Constants;
import org.jboss.as.patching.DirectoryStructure;
import org.jboss.as.patching.installation.AddOn;
import org.jboss.as.patching.installation.InstalledIdentity;
import org.jboss.as.patching.installation.InstalledIdentityImpl;
import org.jboss.as.patching.installation.Layer;
import org.jboss.as.patching.installation.LayerInfo;
import org.jboss.as.patching.installation.PatchableTarget;
import org.jboss.as.patching.runner.PatchUtils;
import org.jboss.staxmapper.XMLElementReader;
import org.jboss.staxmapper.XMLElementWriter;
import org.jboss.staxmapper.XMLExtendedStreamReader;
import org.jboss.staxmapper.XMLExtendedStreamWriter;
/**
* @author Emanuel Muckenhuber
*/
class RollbackPatchXml_1_0 extends PatchXmlUtils implements XMLStreamConstants, XMLElementReader<PatchXml.Result<PatchMetadataResolver>>, XMLElementWriter<RollbackPatch> {
static enum Element {
ADD_ON("add-on"),
IDENTITY("identity"),
INSTALLATION("installation"),
LAYER("layer"),
PATCH("patch"),
// default unknown element
UNKNOWN(null),
;
public final String name;
Element(String name) {
this.name = name;
}
static Map<String, Element> elements = new HashMap<String, Element>();
static {
for(Element element : Element.values()) {
if(element != UNKNOWN) {
elements.put(element.name, element);
}
}
}
static Element forName(String name) {
final Element element = elements.get(name);
return element == null ? UNKNOWN : element;
}
}
static enum Attribute {
NAME("name"),
PATCHES("patches"),
RELEASE_ID("release-id"),
// default unknown attribute
UNKNOWN(null);
private final String name;
Attribute(String name) {
this.name = name;
}
static Map<String, Attribute> attributes = new HashMap<String, Attribute>();
static {
for(Attribute attribute : Attribute.values()) {
if(attribute != UNKNOWN) {
attributes.put(attribute.name, attribute);
}
}
}
static Attribute forName(String name) {
final Attribute attribute = attributes.get(name);
return attribute == null ? UNKNOWN : attribute;
}
}
@Override
public void readElement(XMLExtendedStreamReader reader, PatchXml.Result<PatchMetadataResolver> factory) throws XMLStreamException {
final RollbackPatchBuilder builder = new RollbackPatchBuilder();
doReadElement(reader, builder, factory.getOriginalIdentity());
factory.setResult(builder);
}
@Override
protected void handleRootElement(String localName, XMLExtendedStreamReader reader, PatchBuilder patch, InstalledIdentity originalIdentity) throws XMLStreamException {
final RollbackPatchBuilder builder = (RollbackPatchBuilder) patch;
final Element element = Element.forName(localName);
if (element == Element.INSTALLATION) {
final InstalledIdentity identity = processInstallation(reader, originalIdentity);
builder.setIdentity(identity);
} else {
throw unexpectedElement(reader);
}
}
@Override
public void writeContent(final XMLExtendedStreamWriter writer, final RollbackPatch rollbackPatch) throws XMLStreamException {
// Get started ...
writer.writeStartDocument();
writer.writeStartElement(Element.PATCH.name);
writer.writeDefaultNamespace(PatchXml.Namespace.ROLLBACK_1_2.getNamespace());
writePatch(writer, rollbackPatch);
writeInstallation(writer, rollbackPatch.getIdentityState());
// Done
writer.writeEndElement();
writer.writeEndDocument();
}
static InstalledIdentity processInstallation(final XMLExtendedStreamReader reader, InstalledIdentity originalIdentity) throws XMLStreamException {
LayerInfo identity = null;
final Map<String, LayerInfo> layers = new LinkedHashMap<String, LayerInfo>();
final Map<String, LayerInfo> addOns = new LinkedHashMap<String, LayerInfo>();
while (reader.hasNext() && reader.nextTag() != END_ELEMENT) {
final Element element = Element.forName(reader.getLocalName());
switch (element) {
case IDENTITY:
identity = parseTargetInfo(reader, originalIdentity, element);
break;
case LAYER: {
final LayerInfo info = parseTargetInfo(reader, originalIdentity, element);
layers.put(info.getName(), info);
break;
} case ADD_ON:
final LayerInfo info = parseTargetInfo(reader, originalIdentity, element);
addOns.put(info.getName(), info);
break;
default:
throw unexpectedElement(reader);
}
}
//
final DirectoryStructure structure = identity.getDirectoryStructure();
final WrappedIdentity installation = new WrappedIdentity(identity, structure);
for (final Map.Entry<String, LayerInfo> entry : layers.entrySet()) {
installation.putLayer(entry.getKey(), entry.getValue());
}
for (final Map.Entry<String, LayerInfo> entry : addOns.entrySet()) {
installation.putAddOn(entry.getKey(), entry.getValue());
}
return installation;
}
static LayerInfo parseTargetInfo(final XMLExtendedStreamReader reader, InstalledIdentity originalIdentity, Element target) throws XMLStreamException {
String name = null;
final Properties properties = new Properties();
final int count = reader.getAttributeCount();
for (int i = 0; i < count; i++) {
final String value = reader.getAttributeValue(i);
final Attribute attribute = Attribute.forName(reader.getAttributeLocalName(i));
switch (attribute) {
case NAME:
name = value;
break;
case PATCHES:
properties.put(Constants.PATCHES, value);
break;
case RELEASE_ID:
properties.put(Constants.CUMULATIVE, value);
break;
default:
throw unexpectedAttribute(reader, i);
}
}
requireNoContent(reader);
final DirectoryStructure dirStructure;
if(originalIdentity != null) {
switch(target) {
case LAYER:
dirStructure = originalIdentity.getLayer(name).getDirectoryStructure();
break;
case ADD_ON:
dirStructure = originalIdentity.getAddOn(name).getDirectoryStructure();
break;
default:
dirStructure = originalIdentity.getIdentity().getDirectoryStructure();
}
} else {
dirStructure = null;
}
final LayerInfo.TargetInfo info = LayerInfo.loadTargetInfo(properties, dirStructure);
return new LayerInfo(name, info, dirStructure);
}
static void writeInstallation(final XMLExtendedStreamWriter writer, final InstalledIdentity identity) throws XMLStreamException {
writer.writeStartElement(Element.INSTALLATION.name);
// identity
writeTargetInfo(writer, Element.IDENTITY, identity.getIdentity());
// layers
for (final Layer layer : identity.getLayers()) {
writeTargetInfo(writer, Element.LAYER, layer);
}
// addons
for (final AddOn addOn : identity.getAddOns()) {
writeTargetInfo(writer, Element.ADD_ON, addOn);
}
writer.writeEndElement();
}
static void writeTargetInfo(final XMLExtendedStreamWriter writer, final Element element, final PatchableTarget target) throws XMLStreamException {
try {
final PatchableTarget.TargetInfo info = target.loadTargetInfo();
//
writer.writeEmptyElement(element.name);
writer.writeAttribute(Attribute.NAME.name, target.getName());
writer.writeAttribute(Attribute.RELEASE_ID.name, info.getCumulativePatchID());
if (! info.getPatchIDs().isEmpty()) {
writer.writeAttribute(Attribute.PATCHES.name, PatchUtils.asString(info.getPatchIDs()));
}
} catch (IOException e) {
throw new XMLStreamException(e);
}
}
static class WrappedIdentity extends InstalledIdentityImpl {
final PatchableTarget identity;
WrappedIdentity(final PatchableTarget identity, final DirectoryStructure structure) {
super(new org.jboss.as.patching.installation.Identity() {
@Override
public String getVersion() {
return null;
}
@Override
public String getName() {
return identity.getName();
}
@Override
public TargetInfo loadTargetInfo() throws IOException {
return identity.loadTargetInfo();
}
@Override
public DirectoryStructure getDirectoryStructure() {
return identity.getDirectoryStructure();
}
}, Collections.<String>emptyList(), structure == null ? null : structure.getInstalledImage());
this.identity = identity;
}
@Override
protected Layer putLayer(String name, Layer layer) {
return super.putLayer(name, layer);
}
@Override
protected AddOn putAddOn(String name, AddOn addOn) {
return super.putAddOn(name, addOn);
}
}
static class RollbackPatchBuilder extends PatchBuilder {
protected InstalledIdentity identity;
void setIdentity(InstalledIdentity identity) {
this.identity = identity;
}
@Override
public Patch build() {
final Patch patch = super.build();
return new PatchImpl.RollbackPatchImpl(patch, identity);
}
}
}