Infrastructure at your Service

Stephane Biehler

Java 1.8 Utility classes: XML, ZIP, BufferedImage and Download

When I write code, I usually need some utility classes to ease the development. So here I will share my most used classes. Hope this will help you as well!

XML store and load

I strongly use XML for storing configurations or even data, so I made an helper which can store and load any classes (with annotations) as XML, using generics

import java.io.File;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

/**
 * Utility class to provide some handy method about xml management
 * 
 * @author sbi
 *
 */
public class XmlHelper {

	/**
	 * Generic method to store xml files based on classes with XML anotations
	 * 
	 * @param element - The object to store as xml
	 * @param file - The file path where to store the xml
	 * @throws JAXBException
	 */
	public static  void storeXml(T element, File file) throws JAXBException {
		JAXBContext jaxbContext = JAXBContext.newInstance(element.getClass());
		Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
		jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
		jaxbMarshaller.marshal(element, file);
	}
	
	/**
	 * Generic method to load xml and transform it into an object which was declared with annotations
	 * @param clazz - The class of the object in which we want the xml to be casted
	 * @param file - The XML file located on the file system
	 * @return An object converted from XML
	 * @throws JAXBException
	 */
	@SuppressWarnings("unchecked")
	public static  T loadXml(Class clazz, File file) throws JAXBException {
		JAXBContext jaxbContext = JAXBContext.newInstance(clazz);
		Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
		return (T) jaxbUnmarshaller.unmarshal(file);
	}
}

And here an example of a basic class that can be written as XML. I’ve added 3 kinds of elements:

  • XMLRootElement: It sets this class as XML enabled. It is mandatory.
  • XMLElement: Basic XML element which will create the hierarchy
  • XMLAttribute: Attributes will be located inside the declaration of a new item
  • XMLElementWrapper: It allows to store lists of objects. You can even add your own object but you will have to add anotations to the underlying object as well. Like you did for this class.
import java.util.ArrayList;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name="XMLClass")
public class XMLClass {

	private String name;
	private int id;
	private ArrayList xmlElements;
	
	public ArrayList getXmlElements() {
		return xmlElements;
	}
	
	@XmlElementWrapper(name="ElementList")
	@XmlElement(name="Element")
	public void setXmlElements(ArrayList xmlElements) {
		this.xmlElements = xmlElements;
	}
	
	public int getId() {
		return id;
	}
	
	@XmlAttribute(name="ID")
	public void setId(int id) {
		this.id = id;
	}
	
	public String getName() {
		return name;
	}
	
	@XmlElement(name="Name")
	public void setName(String name) {
		this.name = name;
	}
}

An example of usage:

XMLClass cl = new XMLClass();
cl.setId(1234);
cl.setName("MyXMLExample");
cl.setXmlElements(new ArrayList());
cl.getXmlElements().add("Element1");
cl.getXmlElements().add("Element2");
cl.getXmlElements().add("Element3");
cl.getXmlElements().add("Element4");
try {
	XmlHelper.storeXml(cl, new File("xmlexample.xml"));
} catch (JAXBException e) {
	e.printStackTrace();
}

Result:

<XMLClass ID="1234">
    <Name>MyXMLExample</Name>
    <ElementList>
        <Element>Element1</Element>
        <Element>Element2</Element>
        <Element>Element3</Element>
        <Element>Element4</Element>
    </ElementList>
</XMLClass>

Zipping and Unzipping

Another handy class to manage zips in Java. I will not cover this one as it is self explanatory. Just call the zip function with a source path and the target path (e.g: my_path/myZip.zip).

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
/**
 * Utility class to provide some handy method about zip management
 * Credit goes to: https://www.baeldung.com/java-compress-and-uncompress
 * 
 * @author sbi
 *
 */
public class ZipHelper {
	
	/**
	 * Zips a folder. The folder will be contained inside the zip.
	 * 
	 * @param sourceFile - The path to the source folder to zip
	 * @param target - The name and path to the target zipped file. e.g: path_to_file/myZip.zip
	 * @throws IOException - If the zipping failed
	 */
	public static void zipFolder(String sourceFile, String target) throws IOException {
        FileOutputStream fos = new FileOutputStream(target);
        ZipOutputStream zipOut = new ZipOutputStream(fos);
        File fileToZip = new File(sourceFile);
 
        zipFile(fileToZip, fileToZip.getName(), zipOut);
        zipOut.close();
        fos.close();
    }
 
	/**
	 * Internal zip method to zip a specific file into the folder
	 * 
	 * @param fileToZip - The file to zip
	 * @param fileName - The name of the file
	 * @param zipOut - The outputstream of the current zipping process
	 * @throws IOException - If the zipping failed
	 */
    private static void zipFile(File fileToZip, String fileName, ZipOutputStream zipOut) throws IOException {
        if (fileToZip.isHidden()) {
            return;
        }
        if (fileToZip.isDirectory()) {
            if (fileName.endsWith("/")) {
                zipOut.putNextEntry(new ZipEntry(fileName));
                zipOut.closeEntry();
            } else {
                zipOut.putNextEntry(new ZipEntry(fileName + "/"));
                zipOut.closeEntry();
            }
            File[] children = fileToZip.listFiles();
            for (File childFile : children) {
                zipFile(childFile, fileName + "/" + childFile.getName(), zipOut);
            }
            return;
        }
        FileInputStream fis = new FileInputStream(fileToZip);
        ZipEntry zipEntry = new ZipEntry(fileName);
        zipOut.putNextEntry(zipEntry);
        byte[] bytes = new byte[1024];
        int length;
        while ((length = fis.read(bytes)) >= 0) {
            zipOut.write(bytes, 0, length);
        }
        fis.close();
    }
    
    /**
     * Unzips a zip file
     * 
     * @param fileZip - The zip file to unzip
     * @param destDir - The path to where the zip file will be unzipped
     * @throws IOException - If the zipping failed
     */
    public static void unzipFolder(String fileZip, String destDir) throws IOException {
        byte[] buffer = new byte[1024];
        ZipInputStream zis = new ZipInputStream(new FileInputStream(fileZip));
        ZipEntry zipEntry = zis.getNextEntry();
        while (zipEntry != null) {
            File newFile = newFile(new File(destDir), zipEntry);
            FileOutputStream fos = new FileOutputStream(newFile);
            int len;
            while ((len = zis.read(buffer)) > 0) {
                fos.write(buffer, 0, len);
            }
            fos.close();
            zipEntry = zis.getNextEntry();
        }
        zis.closeEntry();
        zis.close();
    }
    
    /**
     * Internal unzip method used to unzip a specific file
     * 
     * @param destinationDir
     * @param zipEntry
     * @return
     * @throws IOException
     */
    private static File newFile(File destinationDir, ZipEntry zipEntry) throws IOException {
        File destFile = new File(destinationDir, zipEntry.getName());
         
        String destDirPath = destinationDir.getCanonicalPath();
        String destFilePath = destFile.getCanonicalPath();
         
        if (!destFilePath.startsWith(destDirPath + File.separator)) {
            throw new IOException("Entry is outside of the target dir: " + zipEntry.getName());
        }
         
        return destFile;
    }
}

Storing and loading BufferedImage as string

I came across a point where I had to manage Images inside a program. I wanted to store images in a handy way other than image formats. I wanted to store it as a String to be able to store it inside an XML file. And transform it on the fly as image again during runtime. So here are 2 simple functions to store and load PNG images. You can adapt it for other formats of course:

public static String imageToString(BufferedImage img) throws IOException {
	final ByteArrayOutputStream os = new ByteArrayOutputStream();
	ImageIO.write(img, "png", os);
	return Base64.getEncoder().encodeToString(os.toByteArray());
}

public static BufferedImage stringToImage(String text) throws IOException {
	byte[] imageData = Base64.getDecoder().decode(text);
	ByteArrayInputStream bais = new ByteArrayInputStream(imageData);
	return ImageIO.read(bais);
}

I use Base64 to avoid having strange character results, it’s then stored on a One Line String which is more beautiful. If you store it without Base64 in an XML document, it will be difficult to load it again, as it doesn’t support strange characters. And it seems to be a bit more condenced, resulting in lower size footprint.

Download file from URL

This one is for downloading a file from an URL, pretty easy to use. Provide the url as the Source and specify a target file.

public static void downloadFileTo(String source, String target) throws IOException {
	BufferedInputStream inputStream = new BufferedInputStream(new URL(source).openStream());
	File file = new File(target);
	file.getParentFile().mkdirs();
	file.createNewFile();
	FileOutputStream fileOS = new FileOutputStream(file,false);
	byte data[] = new byte[1024];
	int byteContent;
	while ((byteContent = inputStream.read(data, 0, 1024)) != -1) {
		fileOS.write(data, 0, byteContent);
	}
	fileOS.close();
}

Log4j2 external file configuration

I came to the point where I had to configure log4j2 to use a specific file outside the generated jar file. I don’t really like embedded configuration files as you cannot edit them on the fly. So here is a function to specify the location of the log4j2.xml file. You will have to call it at the start of your program so you can use logging as soon as possible:

private static Logger log;
private void configureLogging(String location) throws FileNotFoundException, IOException {
	// Set configuration file for log4j2
	ConfigurationSource source = new ConfigurationSource(new FileInputStream(location));
	Configurator.initialize(null, source);
	log = LogManager.getLogger(YourClass.class);
}

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Stephane Biehler
Stephane Biehler

Consultant