/**
* Copyright (c) 2011 Martin M Reed
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.hardisonbrewing.signingserver.ui;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import javax.microedition.io.Connector;
import net.hardisonbrewing.signingserver.SigservApplication;
import net.hardisonbrewing.signingserver.model.JAD;
import net.hardisonbrewing.signingserver.model.JAD.COD;
import net.hardisonbrewing.signingserver.model.SigningAuthority;
import net.hardisonbrewing.signingserver.service.Properties;
import net.hardisonbrewing.signingserver.service.narst.Signer;
import net.rim.device.api.system.Display;
import net.rim.device.api.ui.DrawStyle;
import net.rim.device.api.ui.Font;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.component.ListField;
import net.rim.device.api.ui.component.ListFieldCallback;
import net.rim.device.api.ui.container.MainScreen;
import org.apache.commons.threadpool.DefaultThreadPool;
import org.apache.commons.threadpool.ThreadPool;
import org.metova.mobile.util.io.IOUtility;
public class CodSigningScreen extends MainScreen {
private ListField listField;
private final JAD jad;
private final SigningAuthority[] signingAuthorities;
private final Hashtable authoritySigningAttempts;
private SigningAttempt[] signingAttempts;
private boolean startedThreads;
public CodSigningScreen(JAD jad, SigningAuthority[] signingAuthorities) {
this.jad = jad;
this.signingAuthorities = signingAuthorities;
COD[] cods = jad.getCODs();
signingAttempts = new SigningAttempt[cods.length * signingAuthorities.length];
authoritySigningAttempts = new Hashtable();
for (int i = 0, k = 0; i < cods.length; i++) {
for (int j = 0; j < signingAuthorities.length; j++) {
SigningAttempt signingAttempt = new SigningAttempt();
signingAttempt.cod = cods[i];
signingAttempt.signingAuthority = signingAuthorities[j];
String signingAuthorityKey = signingAttempt.signingAuthority.key;
Vector vector = (Vector) authoritySigningAttempts.get( signingAuthorityKey );
if ( vector == null ) {
vector = new Vector();
authoritySigningAttempts.put( signingAuthorityKey, vector );
}
vector.addElement( signingAttempt );
signingAttempts[k++] = signingAttempt;
}
}
listField = new ListField();
listField.setCallback( new MyListFieldCallback() );
listField.setSize( signingAttempts.length );
listField.setRowHeight( 30 );
add( listField );
}
protected void onVisibilityChange( boolean visible ) {
super.onVisibilityChange( visible );
if ( visible && !startedThreads ) {
startedThreads = true;
ThreadPool threadPool = new DefaultThreadPool( signingAuthorities.length );
for (int i = 0; i < signingAuthorities.length; i++) {
threadPool.invokeLater( new SignCodsTask( signingAuthorities[i] ) );
}
}
}
private String getSigningStatusText( int status ) {
switch (status) {
case SigningAttempt.STATUS_SENDING:
return "Sending";
case SigningAttempt.STATUS_COMPLETE:
return "Signed";
case SigningAttempt.STATUS_FAILED:
return "Failed";
case SigningAttempt.STATUS_WAITING:
return "Waiting";
default:
return "Unknown";
}
}
private int getSigningAuthorityKeyWidth( Font font ) {
int result = 0;
for (int i = 0; i < signingAuthorities.length; i++) {
result = Math.max( result, font.getAdvance( signingAuthorities[i].key ) );
}
return result;
}
private int getSigningStatusWidth( Font font ) {
int result = 0;
for (int i = 0; i <= SigningAttempt.STATUS_COMPLETE; i++) {
result = Math.max( result, font.getAdvance( getSigningStatusText( i ) ) );
}
return result;
}
private final class MyListFieldCallback implements ListFieldCallback {
public void drawListRow( ListField listField, Graphics graphics, int index, int y, int width ) {
int rowHeight = listField.getRowHeight( index );
SigningAttempt signingAttempt = signingAttempts[index];
COD cod = signingAttempt.cod;
SigningAuthority signingAuthority = signingAttempt.signingAuthority;
Font font = graphics.getFont();
String status = getSigningStatusText( signingAttempt.status );
int statusWidth = getSigningStatusWidth( font );
int statusX = width - ( statusWidth + 10 );
graphics.drawText( status, statusX, y );
int keyDividerX = statusX - 10;
graphics.drawLine( keyDividerX, y, keyDividerX, y + rowHeight );
int keyWidth = getSigningAuthorityKeyWidth( font );
int keyX = keyDividerX - ( keyWidth + 10 );
graphics.drawText( signingAuthority.key, keyX, y );
int filenameDividerX = keyX - 10;
graphics.drawLine( filenameDividerX, y, filenameDividerX, y + rowHeight );
int filenameWidth = filenameDividerX - 20;
int filenameX = 10;
graphics.drawText( cod.filename, filenameX, y, DrawStyle.ELLIPSIS, filenameWidth );
}
public Object get( ListField listField, int index ) {
return signingAttempts[index];
}
public int getPreferredWidth( ListField listField ) {
return Display.getWidth();
}
public int indexOfList( ListField listField, String prefix, int start ) {
return -1;
}
}
private final class SigningAttempt {
public static final int STATUS_WAITING = 0;
public static final int STATUS_SENDING = 1;
public static final int STATUS_FAILED = 2;
public static final int STATUS_COMPLETE = 3;
public COD cod;
public SigningAuthority signingAuthority;
public int status = STATUS_WAITING;
public void updateStatus( int status ) {
this.status = status;
invalidate();
}
}
private final class SignCodsTask implements Runnable {
private SigningAuthority signingAuthority;
public SignCodsTask(SigningAuthority signingAuthority) {
this.signingAuthority = signingAuthority;
}
public void run() {
Signer signer = new Signer();
signer.url = signingAuthority.url;
signer.signerId = signingAuthority.key;
signer.clientId = Long.toString( signingAuthority.clientId );
Vector signingAttempts = (Vector) authoritySigningAttempts.get( signingAuthority.key );
Enumeration enumerator = signingAttempts.elements();
while (enumerator.hasMoreElements()) {
SigningAttempt signingAttempt = (SigningAttempt) enumerator.nextElement();
COD cod = signingAttempt.cod;
String jadDirectory = jad.filePath.substring( 0, jad.filePath.lastIndexOf( '/' ) );
String filePath = jadDirectory + "/" + cod.filename;
for (int i = 0; i < 5; i++) {
InputStream inputStream = null;
try {
signingAttempt.updateStatus( SigningAttempt.STATUS_SENDING );
inputStream = Connector.openInputStream( filePath );
Properties response = signer.requestSignature( inputStream );
synchronized (signingAttempt.cod) {
signer.applySignature( response, filePath );
}
signingAttempt.updateStatus( SigningAttempt.STATUS_COMPLETE );
break;
}
catch (Throwable t) {
IOUtility.safeClose( inputStream );
SigservApplication.logEvent( t.toString() );
}
}
if ( signingAttempt.status != SigningAttempt.STATUS_COMPLETE ) {
signingAttempt.updateStatus( SigningAttempt.STATUS_FAILED );
}
}
}
}
}