CreateProcessAsUser function to run a GUI program from a service - java

Below is the code I am using to run a GUI app from service application. I am passing cmd string "C:\Windows\notepad.exe".
It is not opening the Notepad and not even giving any error. hToken is null even after using WTSQueryUserToken.
Here is a documentation link for create process as user : https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessasusera
private void cpasUser(String cmd) {
HANDLE h = null;
final HANDLEByReference childStdInRead = new HANDLEByReference();
final HANDLEByReference childStdInWrite = new HANDLEByReference();
final HANDLEByReference childStdOutRead = new HANDLEByReference();
final HANDLEByReference childStdOutWrite = new HANDLEByReference();
final int HANDLE_FLAG_INHERIT = 0x00000001;
final int HANDLE_FLAG_PROTECT_FROM_CLOSE = 0x00000002;
final int BUFSIZE = 4096;
final int GENERIC_READ = 0x80000000;
final int FILE_ATTRIBUTE_READONLY = 1;
final int OPEN_EXISTING = 3;
final DWORD STD_OUTPUT_HANDLE = new DWORD(-11);
final int STARTF_USESTDHANDLES = 0x00000100;
String szCmdline = cmd;
PROCESS_INFORMATION processInformation = new PROCESS_INFORMATION();
STARTUPINFO startupInfo = new STARTUPINFO();
startupInfo.cb = new DWORD(processInformation.size());
startupInfo.hStdError = childStdOutWrite.getValue();
startupInfo.hStdOutput = childStdOutWrite.getValue();
startupInfo.hStdInput = childStdInRead.getValue();
startupInfo.dwFlags |= STARTF_USESTDHANDLES;
// Create the child process.
HANDLE hToken = null;
MyWtsapi32 mw = MyWtsapi32.INSTANCE;
mw.WTSQueryUserToken(Kernel32Ext.INSTANCE.WTSGetActiveConsoleSessionId(), hToken) ;
//be sure that the handle is correct ! (can be the issue)
if (hToken == null) logger.info("Token error.");
if (!Advapi32.INSTANCE.CreateProcessAsUser(
hToken,
szCmdline,
null,
null,
null,
true,
32,
null,
null,
startupInfo,
processInformation)){
// System.err.println(Advapi32.INSTANCE.GetLastError());
logger.error("Cannot create process as User ");
logger.error("error code "+Native.getLastError());
}
MyWtsApi32.java
public interface MyWtsapi32 extends Wtsapi32 {
// Your own instance to access your functions
MyWtsapi32 INSTANCE = Native.load("Wtsapi32", MyWtsapi32.class, W32APIOptions.DEFAULT_OPTIONS);
// From https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messagebox
int MB_OK = 0;
// The function to send the message
boolean WTSSendMessageW(HANDLE hServer, int SessionId,
LPWSTR pTitle, int TitleLength,
LPWSTR pMessage, int MessageLength,
int Style, int Timeout, IntByReference pResponse, boolean bWait);
boolean WTSQueryUserToken(long SessionId,HANDLE hServer);
}
Kernel32Ext.java
public interface Kernel32Ext extends Kernel32{
Kernel32Ext INSTANCE = Native.load("Kernel32",Kernel32Ext.class,W32APIOptions.DEFAULT_OPTIONS);
int WTSGetActiveConsoleSessionId();
}

When your JNA function mappings don't work, the first debugging step should be to check your function mappings.
WTSQueryUserToken is defined as:
BOOL WTSQueryUserToken(
ULONG SessionId,
PHANDLE phToken
);
The Windows type ULONG is an unsigned 32-bit integer; it should be mapped as int, not long.
PHANDLE is a pointer to a HANDLE, not the handle itself. The correct JNA mapping is HANDLEByReference.
So your interface function mapping should be:
boolean WTSQueryUserToken(int SessionId, HANDLEByReference hServer);
And your code to call it should be:
HANDLEByReference phToken = new HANDLEByReference();
MyWtsapi32 mw = MyWtsapi32.INSTANCE;
// you should probably check the return value here
// on failure throw LastErrorException
mw.WTSQueryUserToken(Kernel32Ext.INSTANCE.WTSGetActiveConsoleSessionId(), phToken);
// Extract the HANDLE for use in later code
HANDLE hToken = phToken.getValue();

Related

JNA, Mapping and pointers references

I need to use DLL inside my Java application. DLL is exporting some set of functions, authors called it "Direct DLL API". I'm trying to define in java equivalent of following function declaration:
int XcCompress( HXCEEDCMP hComp, const BYTE* pcSource, DWORD dwSourceSize, BYTE** ppcCompressed, DWORD* pdwCompressedSize, BOOL bEndOfData );
Inside my interface that extends Library I declared it as follows:
int XcCompress(WString hComp, Pointer pcSource, int dwSourceSize, Pointer[] ppcCompressed, IntByReference pdwCompressedSize, boolean bEndOfData);
Problem is everytime I get an error:
Exception in thread "main" java.lang.Error: Invalid memory access
So basically I'm stuck at this point.
HXCEEDCMP hComp - is suppose to store handler to the function, and works fine as WString for init DLL / destroying DLL functions so I kept it like this.
The header reference "creature" is:
typedef HXCEEDCMP ( XCD_WINAPI *LPFNXCCREATEXCEEDCOMPRESSIONW )( const WCHAR* );
const BYTE* pcSource - is the source data for compression, inside my code I instantiate it this way:
private static Pointer setByteArrayPointer(String dataToCompress) {
Pointer pointer = new Memory(1024);
pointer.write(0, dataToCompress.getBytes(), 0,
dataToCompress.getBytes().length);
return pointer;
}
DWORD dwSourceSize - for this im getting reserved Memory size in this way:
String testData = "ABCDABCDABCDAAD";
Pointer source = setByteArrayPointer(testData);
(int) ((Memory)source).size()
BYTE** ppcCompressed - function should populate ppcCompressed reference after work is done. I assume I made a mistake there, by doing it in this way:
Pointer[] compressed = {new Pointer(1024), new Pointer(1024)};
DWORD* pdwCompressedSize - returned by function size of compressed data. I map it in this way:
IntByReference intByReference = new IntByReference();
Not sure if it is good idea aswell..
BOOL bEndOfData - i need to set it to true.
So finally my method call, which returns an error looks like this:
xceedApiDll.XcCompress(handle, source, (int) ((Memory)source).size(), compressed, intByReference, true);
Any help will be appreciated. Thank you.
I think i solved the issue (thanks for comments guys). Maybe for someone using this library it will be useful:
In the end the main problem was with handler declaration and the ppcCompressed value.
I used the following solution which works fine for me:
Method declarations inside java interface:
int XcCompress(Pointer hComp, byte[] pcSource, int dwSourceSize, PointerByReference ppcCompressed, IntByReference pdwCompressedSize, int bEndOfData);
int XcUncompress(Pointer hComp, byte[] pcSource, int dwSourceSize, PointerByReference ppcUncompressed, IntByReference pdwUncompressedSize, int bEndOfdata);
Usage:
private static final XceedFunctions XCEED_DLL_API;
static {
XCEED_DLL_API = Native.load("XceedZipX64", XceedFunctions.class);
}
private static final String TEST_DATA = "abcabcddd";
//Data pointers
private static Pointer compHandle;
private static byte[] baSource = TEST_DATA.getBytes();
private static PointerByReference pbrCompressed = new PointerByReference();
private static PointerByReference pbrUncompressed = new PointerByReference();
private static IntByReference ibrCompressedSize = new IntByReference();
private static IntByReference ibrUncompressedSize = new IntByReference();
public static void main(String[] args) {
try {
boolean isSuccessfulInit = XCEED_DLL_API.XceedZipInitDLL();
if(isSuccessfulInit) {
compHandle = XCEED_DLL_API.XcCreateXceedCompressionW(new WString("YOUR_LICENCE_KEY_HERE"));
int compressionResult = XCEED_DLL_API.XcCompress(compHandle, baSource, baSource.length, pbrCompressed, ibrCompressedSize, 1);
byte[] compressed = getDataFromPbr(pbrCompressed, ibrCompressedSize);
System.out.println("Compression result: " + compressionResult + " Data: " + new String(compressed));
int decompressionResult = XCEED_DLL_API.XcUncompress(compHandle, compressed, compressed.length, pbrUncompressed, ibrUncompressedSize, 1);
byte[] uncompressed = getDataFromPbr(pbrUncompressed, ibrUncompressedSize);
System.out.println("Decompression result: " + decompressionResult + " Data: " + new String(uncompressed));
}
} finally {
System.out.println("Free memory and shutdown");
if(compHandle != null) {
XCEED_DLL_API.XcDestroyXceedCompression(compHandle);
}
XCEED_DLL_API.XceedZipShutdownDLL();
}
}
private static byte[] getDataFromPbr(PointerByReference pbr, IntByReference ibr) {
return pbr.getValue().getByteArray(0, ibr.getValue());
}
Example output:
Compression result: 0 Data: KLJNLJNII yK
Decompression result: 0 Data: abcabcddd
Free memory and shutdown

How to create separate change list when using the API?

I am trying to create a Groovy script that takes a list of change lists from our trunk and merges them one at a time into a release branch. I would like to have all the change lists locally because I want to run a test build before submitting upstream. However, whenever I run the script I find that when I look in P4V all the merges have been placed in the default change list. How can I keep them separate?
My code (in Groovy but using the Java API) is as follows:
final changeListNumbers = [ 579807, 579916, 579936 ]
final targetBranch = "1.0.7"
changeListNumbers.each { changeListNumber ->
final existingCl = server.getChangelist( changeListNumber )
final cl = new Changelist(
IChangelist.UNKNOWN,
client.getName(),
server.userName,
ChangelistStatus.NEW,
new Date(),
"${existingCl.id} - ${existingCl.description}",
false,
server
);
cl.fileSpecs = mergeChangeListToBranch( client, cl, changeListNumber, targetBranch )
}
def List<IFileSpec> mergeChangeListToBranch( final IClient client, final IChangelist changeList, final srcChangeListNumber, final String branchVersion ){
final projBase = '//Clients/Initech'
final trunkBasePath = "$projBase/trunk"
final branchBasePath = "$projBase/release"
final revisionedTrunkPath = "$trunkBasePath/...#$srcChangeListNumber,$srcChangeListNumber"
final branchPath = "$branchBasePath/$branchVersion/..."
println "trunk path: $revisionedTrunkPath\nbranch path is: $branchPath"
mergeFromTo( client, changeList, revisionedTrunkPath, branchPath )
}
def List<IFileSpec> mergeFromTo( final IClient client, final IChangelist changeList,final String sourceFile, final String destFile ){
mergeFromTo(
client,
changeList,
new FileSpec( new FilePath( FilePath.PathType.DEPOT, sourceFile ) ),
new FileSpec( new FilePath( FilePath.PathType.DEPOT, destFile ) )
)
}
def List<IFileSpec> mergeFromTo( final IClient client, final IChangelist changeList, final FileSpec sourceFile, final FileSpec destFile ){
final resolveOptions = new ResolveFilesAutoOptions()
resolveOptions.safeMerge = true
client.resolveFilesAuto(
client.integrateFiles( sourceFile, destFile, null, null ),
// client.integrateFiles( changeList.id, false, null, null, sourceFile, destFile ),
resolveOptions
)
}
If I try to IChangeList.update() I get the following error:
Caught: com.perforce.p4java.exception.RequestException: Error in change specification.
Error detected at line 7.
Invalid status 'new'.
If instead of using IChangelist.UNKNOWN to existingCl.id + 10000 (which is larger than any existing change list number currently in use) then I get
Caught: com.perforce.p4java.exception.RequestException: Tried to update new or default changelist
To create the changelist in the server, call IClient.createChangelist():
final existingCl = server.getChangelist( changeListNumber )
cl = new Changelist(
IChangelist.UNKNOWN,
... snip ...
);
cl = client.createChangelist(cl);
cl.fileSpecs = mergeChangeListToBranch( client, cl, ...
Then to integrate into this particular change:
IntegrateFilesOptions intOpts = new IntegrateFilesOptions()
intOpts.setChangelistId( cl.getId())
client.integrateFiles( sourceFile, destFile, null, intOpts)
That integrateFiles() returns the integrated file(s), so check that the returned IFileSpec.getOpStatus() is FileSpecOpStatus.VALID.

How can I check if I am running 32bit or 64bit java in C#

I am writing my first C# program and I want to check using C# code if I am running a 32 or 64 bit version of java ?
I tried this but when I add this code to my class I am not able to debug it
RegistryKey rk = Registry.LocalMachine;
RegistryKey subKey = rk.OpenSubKey("SOFTWARE\\JavaSoft\\Java Runtime Environment");
string currentVerion = subKey.GetValue("CurrentVersion").ToString();
How can I do it ?
Thanks
It isn't clear how you are going to identify which java.exe you are using - a single machine can have many installed. You may have a specific path, or you may need to either use the JAVA_HOME environment variable, or search PATH, or do a combination of both and give priority to one or the other depending on your requirements.
Once you've got your path to java.exe you can use the technique from Kris Stanton on MSDN (which I will repeat here, but is currently linked at MSDN > "Exploring pe file headers using managed code"):
public enum MachineType
{
Native = 0, I586 = 0x014c, Itanium = 0x0200, x64 = 0x8664
}
public static MachineType GetMachineType(string fileName)
{
// dos header is 64 bytes
// PE header address is 4 bytes
const int PE_PTR_OFFSET = 60;
const int MACHINE_OFFSET = 4;
byte[] data = new byte[4096];
using (Stream stm = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
stm.Read(data, 0, 4096);
}
int PE_HDR_ADDR = BitConverter.ToInt32(data, PE_PTR_OFFSET);
int machineUint = BitConverter.ToUInt16(data, PE_HDR_ADDR + MACHINE_OFFSET);
return (MachineType)machineUint;
}
To find java.exe on the %PATH% variable, you can call FindOnPath("java.exe"):
public static String FindOnPath(string exeName)
{
foreach (string test in (Environment.GetEnvironmentVariable("PATH") ?? "").Split(';'))
{
string path = test.Trim();
if (!String.IsNullOrEmpty(path) && File.Exists(path = Path.Combine(path, exeName)))
return Path.GetFullPath(path);
}
return null;
}
On my machine, the following code
static void Main(string[] args)
{
String path = FindOnPath("java.exe");
Console.WriteLine(path);
Console.WriteLine(GetMachineType(path));
}
writes the following output:
C:\ProgramData\Oracle\Java\javapath\java.exe
x64
You can do it through the registry. I knocked together a quick example for you:
private string GetJavaInstallationPath()
{
string environmentPath = Environment.GetEnvironmentVariable("JAVA_HOME");
if (!string.IsNullOrEmpty(environmentPath))
{
return environmentPath;
}
string javaKey = "SOFTWARE\\JavaSoft\\Java Runtime Environment\\";
using (Microsoft.Win32.RegistryKey rk = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(javaKey))
{
string currentVersion = rk.GetValue("CurrentVersion").ToString();
using (Microsoft.Win32.RegistryKey key = rk.OpenSubKey(currentVersion))
{
return key.GetValue("JavaHome").ToString();
}
}
}
Then to use it, just do the following:
string installPath = GetJavaInstallationPath();
string filePath = System.IO.Path.Combine(installPath, "bin\\Java.exe");
if (System.IO.File.Exists(filePath))
{
// We have a winner
}
I`m use this code:
public static bool CheckJavaInstallation()
{
try
{
//ProcessStartInfo procStartInfo = new ProcessStartInfo("java", " -version"); // Check that any Java installed
//ProcessStartInfo procStartInfo = new ProcessStartInfo("java", "-d32 -version"); // Check that 32 bit Java installed
ProcessStartInfo procStartInfo = new ProcessStartInfo("java", "-d64 -version"); // Check that 64 bit Java installed
procStartInfo.RedirectStandardOutput = true;
procStartInfo.RedirectStandardError = true;
procStartInfo.UseShellExecute = false;
procStartInfo.CreateNoWindow = true;
Process proc = new Process {StartInfo = procStartInfo};
proc.Start();
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
proc.WaitForExit();
return proc.ExitCode == 0;
}
catch (Exception ex)
{
return false;
}
}

How to create an open polyline in GeoTools 12-RC1?

I need to create a GeoJSON stream, which contains data about roads. The roads are like polygons, except they are open (the start and end points are different).
I tried to use the following code to achieve that goal:
protected void addPolyLine(final double[] coords,
final GeometryBuilder builder,
final SimpleFeatureBuilder fbuilder,
final List<SimpleFeature> features,
final String id) {
final double[] modcoords = new double[coords.length+2];
for (int i=0; i < coords.length; i++) {
modcoords[i] = coords[i];
}
modcoords[modcoords.length-2] = coords[0];
modcoords[modcoords.length-1] = coords[1];
final double[] holeStart = new double[] {coords[0], coords[1],
coords[coords.length-2], coords[coords.length - 1]};
final LinearRing shell = builder.linearRing(modcoords);
final LinearRing hole = builder.linearRing(holeStart);
final Polygon polygon = builder.polygon(shell, hole);
fbuilder.add(polygon);
final SimpleFeature feature = fbuilder.buildFeature(id);
features.add(feature);
}
It doesn't work - I get the error
java.lang.IllegalArgumentException: Invalid number of points in LinearRing (found 3 - must be 0 or >= 4)
at com.vividsolutions.jts.geom.LinearRing.validateConstruction(LinearRing.java:114)
at com.vividsolutions.jts.geom.LinearRing.<init>(LinearRing.java:106)
at com.vividsolutions.jts.geom.GeometryFactory.createLinearRing(GeometryFactory.java:341)
at org.geotools.geometry.jts.GeometryBuilder.linearRing(GeometryBuilder.java:199)
When I use builder.lineString(coords), the resulting GeoJSON contains no coordinates as it should.
How can I create a polyline (a line, which connects several point and is not closed) using GeoTools 12-RC1?
Update 1 (05.07.2015 21:22 MSK): This is how I define feature types for points, polygons and lines. Points and polygons work fine, lines don't.
private final static SimpleFeatureType POINT =
createType("Test", "Location:Point");
private final static SimpleFeatureType POLYGON =
createType("Test2", "Location:Polygon");
private final BuildingsReader buildingsReader =
new DefaultBuildingsReader();
private final static SimpleFeatureType LINE =
createType("Test3", "Line");
private static SimpleFeatureType createType(final String x1, final String x2) {
try
{
return DataUtilities.createType(x1, x2);
}
catch (final SchemaException exception) {
exception.printStackTrace();
}
return null;
}
You need to do something with createLineString like:
line=builder.createLineString(Arrays.asList(coords));
fbuilder.add(line);
final SimpleFeature feature = fbuilder.buildFeature(id);
features.add(feature);
You will need to modify your feature type to expect a line instead of a polygon.

Deflate (ZIP) compressing on the fly in Java using InputStream and OutputStream abstraction only. Possible?

I'm currently trying to write a custom streams proxy (let's call it in that way) that can change the content from the given input stream and produce a modified, if necessary, output. This requirement is really necessary because sometimes I have to modify the streams in my application (e.g. compress the data truly on the fly). The following class is pretty easy and it uses internal buffering.
private static class ProxyInputStream extends InputStream {
private final InputStream iStream;
private final byte[] iBuffer = new byte[512];
private int iBufferedBytes;
private final ByteArrayOutputStream oBufferStream;
private final OutputStream oStream;
private byte[] oBuffer = emptyPrimitiveByteArray;
private int oBufferIndex;
ProxyInputStream(InputStream iStream, IFunction<OutputStream, ByteArrayOutputStream> oStreamFactory) {
this.iStream = iStream;
oBufferStream = new ByteArrayOutputStream(512);
oStream = oStreamFactory.evaluate(oBufferStream);
}
#Override
public int read() throws IOException {
if ( oBufferIndex == oBuffer.length ) {
iBufferedBytes = iStream.read(iBuffer);
if ( iBufferedBytes == -1 ) {
return -1;
}
oBufferIndex = 0;
oStream.write(iBuffer, 0, iBufferedBytes);
oStream.flush();
oBuffer = oBufferStream.toByteArray();
oBufferStream.reset();
}
return oBuffer[oBufferIndex++];
}
}
Let's assume we also have a sample test output stream that simply adds a space character before every written byte ("abc" -> " a b c") like this:
private static class SpacingOutputStream extends OutputStream {
private final OutputStream outputStream;
SpacingOutputStream(OutputStream outputStream) {
this.outputStream = outputStream;
}
#Override
public void write(int b) throws IOException {
outputStream.write(' ');
outputStream.write(b);
}
}
And the following test method:
private static void test(final boolean useDeflater) throws IOException {
final FileInputStream input = new FileInputStream(SOURCE);
final IFunction<OutputStream, ByteArrayOutputStream> outputFactory = new IFunction<OutputStream, ByteArrayOutputStream>() {
#Override
public OutputStream evaluate(ByteArrayOutputStream outputStream) {
return useDeflater ? new DeflaterOutputStream(outputStream) : new SpacingOutputStream(outputStream);
}
};
final InputStream proxyInput = new ProxyInputStream(input, outputFactory);
final OutputStream output = new FileOutputStream(SOURCE + ".~" + useDeflater);
int c;
while ( (c = proxyInput.read()) != -1 ) {
output.write(c);
}
output.close();
proxyInput.close();
}
This test method simply reads the file content and writes it to another stream, that's probably can be modified somehow. If the test method is running with useDeflater=false, the expected approach works fine as it's expected. But if the test method is invoked with the useDeflater set on, it behaves really strange and simply writes almost nothing (if omit the header 78 9C). I suspect that the deflater class may not be designed to meet the approach I like to use, but I always believed that ZIP format and the deflate compression are designed to work on-fly.
Probably I'm wrong at some point with the specifics of the deflate compression algorithm. What do I really miss?.. Perhaps there could be another approach to write a "streams proxy" to behave exactly as I want it to work... How can I compress the data on the fly being limited with the streams only?
Thanks in advance.
UPD: The following basic version works pretty nice with deflater and inflater:
public final class ProxyInputStream<OS extends OutputStream> extends InputStream {
private static final int INPUT_BUFFER_SIZE = 512;
private static final int OUTPUT_BUFFER_SIZE = 512;
private final InputStream iStream;
private final byte[] iBuffer = new byte[INPUT_BUFFER_SIZE];
private final ByteArrayOutputStream oBufferStream;
private final OS oStream;
private final IProxyInputStreamListener<OS> listener;
private byte[] oBuffer = emptyPrimitiveByteArray;
private int oBufferIndex;
private boolean endOfStream;
private ProxyInputStream(InputStream iStream, IFunction<OS, ByteArrayOutputStream> oStreamFactory, IProxyInputStreamListener<OS> listener) {
this.iStream = iStream;
oBufferStream = new ByteArrayOutputStream(OUTPUT_BUFFER_SIZE);
oStream = oStreamFactory.evaluate(oBufferStream);
this.listener = listener;
}
public static <OS extends OutputStream> ProxyInputStream<OS> proxyInputStream(InputStream iStream, IFunction<OS, ByteArrayOutputStream> oStreamFactory, IProxyInputStreamListener<OS> listener) {
return new ProxyInputStream<OS>(iStream, oStreamFactory, listener);
}
#Override
public int read() throws IOException {
if ( oBufferIndex == oBuffer.length ) {
if ( endOfStream ) {
return -1;
} else {
oBufferIndex = 0;
do {
final int iBufferedBytes = iStream.read(iBuffer);
if ( iBufferedBytes == -1 ) {
if ( listener != null ) {
listener.afterEndOfStream(oStream);
}
endOfStream = true;
break;
}
oStream.write(iBuffer, 0, iBufferedBytes);
oStream.flush();
} while ( oBufferStream.size() == 0 );
oBuffer = oBufferStream.toByteArray();
oBufferStream.reset();
}
}
return !endOfStream || oBuffer.length != 0 ? (int) oBuffer[oBufferIndex++] & 0xFF : -1;
}
}
I don't believe that DeflaterOutputStream.flush() does anything meaningful. the deflater will accumulate data until it has something to write out to the underlying stream. the only way to force the remaining bit of data out is to call DeflaterOutputStream.finish(). however, this would not work for your current implementation, as you can't call finish until you are entirely done writing.
it's actually very difficult to write a compressed stream and read it within the same thread. In the RMIIO project i actually do this, but you need an arbitrarily sized intermediate output buffer (and you basically need to push data in until something comes out compressed on the other end, then you can read it). You might be able to use some of the util classes in that project to accomplish what you want to do.
Why don't use GZipOutputStream?
I'm a little lost. But I should simple use the original outputStream when I don't want to compress and new GZipOutputStream(outputStream) when I DO want to compress. That's all. Anyway, check you are flushing the output streams.
Gzip vs zip
Also: one thing is GZIP (compress a stream, that's what you're doing) and another thing is writing a valid zip file (file headers, file directory, entries (header,data)*). Check ZipOutputStream.
Be careful, if somewhere you use method
int read(byte b[], int off, int len) and in case of exception in line
final int iBufferedBytes = iStream.read(iBuffer);
you will get stuck in infinite loop

Categories

Resources