Tuesday, January 5, 2016

WordPress - add_tag_class() vs preg_replace()

If you got this error/warning:

Warning: preg_replace(): The /e modifier is no longer supported, use preg_replace_callback instead in functions.php on line 209

preg_replace with /e modifier was deprecated in version 5.5:

http://php.net/manual/en/migration55.deprecated.php

"The preg_replace() /e modifier is now deprecated. Instead, use the preg_replace_callback() function."

Looking the documentation:

http://php.net/manual/en/function.preg-replace-callback.php

Then change this:

$regex = "#(.*tag-link[-])(.*)(' title.*)#e";

To this:

$regex = "#(.*tag-link[-])(.*)(' title.*)#";

And change this:

$tagn[] = preg_replace($regex, "('$1$2 label tag-'.get_tag($2)->slug.'$3')", $tag );

To this:

$tagn[] = preg_replace_callback($regex,
    function ($matches) {
        return $matches[1].$matches[2] ." label tag-". get_tag($matches[2])->slug . $matches[3];
    }, $tag);

Cheers!

Thursday, December 6, 2012

PHP + OpenOffice|LibreOffice = EXCEL & PDF Generator

To use OpenOffice from command-line to convert files is like this:

soffice -invisible -convert-to xls myfile.xml

In this case convert a xml file to xls. If you want a PDF file just change to:

soffice -invisible -convert-to pdf myfile.doc

You can use any kind of files supported by OpenOffice as input, and can define the parameter -convert-to to any format supported by Open Office.

I will now just talk about xml to xls, because is very most trick. And to PDF is simple like that above, nothing more to say about it.

To generate a xml valid to Open Office is not easy because the character encode, in my case portuguese iso-8859-15. I had a nightmare, but at end was more simple than seemed.

I save my data in DB utf8_encode then I have to use this function stringToExcelXML to decode and convert to XML special characters format.

Below my PHP code that build the XML compatible with OpenOffice and MSOffice:


error_reporting(E_ALL);
include '../include/config.php';

try {
 $rows = MySQL::Create('SELECT * FROM `Client` WHERE `Name` LIKE ?name ORDER BY `Name`')
  ->Parameter('name', '%'.$_GET['name'].'%', 'string')
  ->Query();
} catch(MySQLException $ex) {
 echo $ex->getMessage();
}

$outputFileName = 'export_'. date ('Ymd_His');

function stringToExcelXML($string){
    $string = htmlspecialchars(utf8_decode($string));
    $string = str_replace("\x01", "\x20", $string);
    return $string;
}

$f = fopen('../export/'. $outputFileName .'.xml', "w");
fwrite($f, '<?xml version="1.0" encoding="iso-8859-15"?>'. chr(13));
fwrite($f, '<?mso-application progid="Excel.Sheet"?>'. chr(13));
fwrite($f, '<Workbook');
fwrite($f, '   xmlns="urn:schemas-microsoft-com:office:spreadsheet"');
fwrite($f, '   xmlns:o="urn:schemas-microsoft-com:office:office"');
fwrite($f, '   xmlns:x="urn:schemas-microsoft-com:office:excel"');
fwrite($f, '   xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"');
fwrite($f, '   xmlns:html="http://www.w3.org/TR/REC-html40">'. chr(13));
fwrite($f, '  <DocumentProperties xmlns="urn:schemas-microsoft-com:office:office">'. chr(13));
fwrite($f, '    <Author>Candidatures</Author>'. chr(13));
fwrite($f, '    <LastAuthor>Candidatures</LastAuthor>'. chr(13));
fwrite($f, '    <Created>'. date('Y-m-d\TH:i:s\Z') .'</Created>'. chr(13));
fwrite($f, '    <Company>Candidatures</Company>'. chr(13));
fwrite($f, '    <Version>1</Version>'. chr(13));
fwrite($f, '  </DocumentProperties>'. chr(13));
fwrite($f, '  <ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">'. chr(13));
fwrite($f, '    <WindowHeight>6795</WindowHeight>'. chr(13));
fwrite($f, '    <WindowWidth>8460</WindowWidth>'. chr(13));
fwrite($f, '    <WindowTopX>120</WindowTopX>'. chr(13));
fwrite($f, '    <WindowTopY>15</WindowTopY>'. chr(13));
fwrite($f, '    <ProtectStructure>False</ProtectStructure>'. chr(13));
fwrite($f, '    <ProtectWindows>False</ProtectWindows>'. chr(13));
fwrite($f, '  </ExcelWorkbook>'. chr(13));
fwrite($f, '  <Styles>'. chr(13));
fwrite($f, '    <Style ss:ID="Default" ss:Name="Normal">'. chr(13));
fwrite($f, '      <Alignment ss:Vertical="Bottom" />'. chr(13));
fwrite($f, '      <Borders />'. chr(13));
fwrite($f, '      <Font />'. chr(13));
fwrite($f, '      <Interior />'. chr(13));
fwrite($f, '      <NumberFormat />'. chr(13));
fwrite($f, '      <Protection />'. chr(13));
fwrite($f, '    </Style>'. chr(13));
fwrite($f, '    <Style ss:ID="s21">'. chr(13));
fwrite($f, '      <Font x:Family="Swiss" ss:Bold="1" />'. chr(13));
fwrite($f, '    </Style>'. chr(13));
fwrite($f, '  </Styles>'. chr(13));
fwrite($f, '  <Worksheet ss:Name="Clients">'. chr(13));
fwrite($f, '    <Table ss:ExpandedColumnCount="2" ss:ExpandedRowCount="'. (isset($rows) ? count($rows) : 0) .'"');
fwrite($f, '         x:FullColumns="1" x:FullRows="1">'. chr(13));
fwrite($f, '      <Row>'. chr(13));
fwrite($f, '        <Cell ss:StyleID="s21">'. chr(13));
fwrite($f, '          <Data ss:Type="String">Id</Data>'. chr(13));
fwrite($f, '        </Cell>'. chr(13));
fwrite($f, '        <Cell ss:StyleID="s21">'. chr(13));
fwrite($f, '          <Data ss:Type="String">Name</Data>'. chr(13));
fwrite($f, '        </Cell>'. chr(13));
fwrite($f, '      </Row>'. chr(13));
if (isset($rows)) {
  foreach($rows as $row) {
    fwrite($f, '      <Row>'. chr(13));
    fwrite($f, '        <Cell ss:StyleID="s21">'. chr(13));
    fwrite($f, '          <Data ss:Type="String">'. $row['Id'] .'</Data>'. chr(13));
    fwrite($f, '        </Cell>'. chr(13));
    fwrite($f, '        <Cell ss:StyleID="s21">'. chr(13));
    fwrite($f, '          <Data ss:Type="String">'. stringToExcelXML($row['Name']) .'</Data>'. chr(13));
    fwrite($f, '        </Cell>'. chr(13));
    fwrite($f, '      </Row>'. chr(13));
  }
}
fwrite($f, '    </Table>'. chr(13));
fwrite($f, '    <WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">'. chr(13));
fwrite($f, '      <Print>'. chr(13));
fwrite($f, '        <ValidPrinterInfo />'. chr(13));
fwrite($f, '        <HorizontalResolution>600</HorizontalResolution>'. chr(13));
fwrite($f, '        <VerticalResolution>600</VerticalResolution>'. chr(13));
fwrite($f, '      </Print>'. chr(13));
fwrite($f, '      <Selected />'. chr(13));
fwrite($f, '      <Panes>'. chr(13));
fwrite($f, '        <Pane>'. chr(13));
fwrite($f, '          <Number>3</Number>'. chr(13));
fwrite($f, '          <ActiveRow>5</ActiveRow>'. chr(13));
fwrite($f, '          <ActiveCol>1</ActiveCol>'. chr(13));
fwrite($f, '        </Pane>'. chr(13));
fwrite($f, '      </Panes>'. chr(13));
fwrite($f, '      <ProtectObjects>False</ProtectObjects>'. chr(13));
fwrite($f, '      <ProtectScenarios>False</ProtectScenarios>'. chr(13));
fwrite($f, '    </WorksheetOptions>'. chr(13));
fwrite($f, '  </Worksheet>'. chr(13));

fclose($f);

$excelFile = '../export/'. $outputFileName .'.xls';

while (true) {
  sleep(1);
  if (file_exists($excelFile)) {
    sleep(100);
    break;
  }
}

header('Content-Disposition: attachment; filename='. $outputFileName .'.xls');
header('Content-Type: application/octet-stream');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-length: '. filesize($excelFile));
ob_clean();
flush();
readfile($excelFile);
unlink($excelFile);

exit();
?>

Attention with worksheet name <Worksheet ss:Name="Clients"> and the number of columns <Table ss:ExpandedColumnCount="2" ... and the encoding="iso-8859-15".

This block of code below will wait the OpenOffice finish the creation of file XLS.

$excelFile = '../export/'. $outputFileName .'.xls';

while (true) {
  sleep(1);
  if (file_exists($excelFile)) {
    sleep(100);
    break;
  }
}

This service made with shell script need still running all time to build XLSs using OpenOffice:

/var/www/services/openoffice_to_xls/openoffice_to_xls.sh
#!/bin/bash

cd /var/www/htdocs/export

while true
do
  for f in `ls *.xml`
  do
    sleep 1
    echo "$f"
    soffice -invisible -convert-to xls "$f"
    sleep 15
    chmod a+rwx *.xls
    rm -f "$f"
  done
  sleep 1
done

Turn the core.sh file as executable:

chmod +x /var/www/services/openoffice_to_xls/core.sh

A start script will be useful:

/var/www/services/openoffice_to_xls/start.sh
#!/bin/bash

rm -f /var/www/services/openoffice_to_xls/nohup.out
nohup /var/www/services/openoffice_to_xls/core.sh &

Turn the start.sh file as executable:

chmod +x /var/www/services/openoffice_to_xls/start.sh

Now just start the service:

/var/www/services/openoffice_to_xls/start.sh

All of this is very trick and to works with MSOffice 2010 and OpenXML... and to this work exists the PHPExcel that is much more elegant:

phpexcel.codeplex.com

But to build office legacy files perhaps these tricks will be useful to do workarounds.

Have fun and good nightmares.

Friday, March 25, 2011

Resolve the IP Number from a Host Name

This sample translate the IP number from www.google.com. But you can even use localhost or localmachinename to gets the local or external IP number of the local machine.

#include <stdio.h>
#include <iostream>
#include <arpa/inet.h>
#include <netdb.h>
int main() {
  char hostName[] = "www.google.com";
  struct hostent *host;
  if ((host = gethostbyname(hostName)) == NULL) {
    fprintf(stderr, "(mini) nslookup failed on '%s'", hostName);
    return 1;
  }
  struct in_addr h_addr;
  h_addr.s_addr = *((unsigned long *) host->h_addr_list[0]);
  const char *ip = inet_ntoa(h_addr);
  std::cout << ip << std::endl;
  return 0;
}

Convert Integer to String without itoa

This solution is based from here.

#include <iostream>
#include <sstream>
int main() {
  int i = 100;
  std::stringstream ss;
  ss << i;
  fprintf(stderr, ss.str().c_str());
}

GNU/C++ Simple Send Mail

See the Microsoft Visual C++ Simple Send Mail here.

I did the class below for GNU C++ based in these links:

SendMail.h:

#ifndef _SENDMAILHEADER
#define _SENDMAILHEADER

#include <sys/socket.h>
#include <arpa/inet.h>
#include <string>
#include <string.h>

#define HELO "HELO\r\n"
#define DATA "DATA\r\n"
#define QUIT "QUIT\r\n"

namespace mail {
class SendMail {
private:
int sock;
char buf[BUFSIZ];
void send_socket(char *s)
{
send(sock, s, strlen(s), 0);
//fprintf(stderr, s);
}
void read_socket()
{
int len = recv(sock, buf, BUFSIZ, 0);
//fprintf(stderr, ((std::string)buf).substr(0, len).c_str());
}
public:
char* HostIp;
unsigned short HostPort;
char* To;
char* From;
char* Subject;
char* Message;
SendMail() {
HostPort = 25;
};
~SendMail() { };
void Send() {
struct sockaddr_in hostAddr;
if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
fprintf(stderr, "SendMail: socket() failed");
return;
}
hostAddr.sin_family = AF_INET;
hostAddr.sin_addr.s_addr = inet_addr(HostIp);
hostAddr.sin_port = htons(HostPort);
if (connect(sock, (struct sockaddr *) &hostAddr, sizeof(struct sockaddr_in)) != 0) {
fprintf(stderr, "SendMail: connect() failed");
return;
}
read_socket();
send_socket((char*)HELO);
read_socket();
send_socket((char*)"MAIL FROM: ");
send_socket(From);
send_socket((char*)"\r\n");
read_socket();
send_socket((char*)"VRFY ");
send_socket(From);
send_socket((char*)"\r\n");
read_socket();
send_socket((char*)"RCPT TO: ");
send_socket(To);
send_socket((char*)"\r\n");
read_socket();
send_socket((char*)DATA);
send_socket((char*)"Subject: ");
send_socket(Subject);
send_socket((char*)"\r\n");
read_socket();
send_socket(Message);
send_socket((char*)"\r\n");
send_socket((char*)".\r\n");
read_socket();
send_socket((char*)QUIT);
read_socket();
close(sock);
};
};
}

#endif


Usage:

#include "SendMail.h"

int main(int argc, char** argv) {
mail::SendMail* sendmail = new mail::SendMail();
sendmail->HostIp = (char*)"192.168.1.10";
sendmail->From = (char*)"mail@address.com";
sendmail->To = (char*)"mail@address.com";
sendmail->Subject = (char*)"Test!";
sendmail->Message = (char*)"Content:\nTest 123...";
sendmail->Send();
return 0;
}

VC++ Simple Send Mail

See the GNU/C++ Simple Send Mail here.

I did the class below for Visual C++ based in these links:

SendMail.h:

#ifndef _SENDMAILHEADER
#define _SENDMAILHEADER

#include "stdafx.h"
#include <stdio.h>
#include <Winsock2.h>
#include <Windows.h>
#include <string>

#pragma comment(lib, "ws2_32.lib")

#define HELO "HELO\r\n"
#define DATA "DATA\r\n"
#define QUIT "QUIT\r\n"

namespace mail {
class SendMail {
private:
int sock;
char buf[BUFSIZ];
void send_socket(char *s)
{
send(sock, s, strlen(s), 0);
//fprintf(stderr, s);
}
void read_socket()
{
int len = recv(sock, buf, BUFSIZ, 0);
//fprintf(stderr, ((std::string)buf).substr(0, len).c_str());
}
public:
char* HostIp;
unsigned short HostPort;
char* To;
char* From;
char* Subject;
char* Message;
SendMail() {
HostPort = 25;
};
~SendMail() { };
void Send() {
struct sockaddr_in hostAddr;
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0) {
fprintf(stderr, "SendMail: WSAStartup() failed");
return;
}
if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
fprintf(stderr, "SendMail: socket() failed");
return;
}
memset(&hostAddr, 0, sizeof(hostAddr));
hostAddr.sin_family = AF_INET;
hostAddr.sin_addr.s_addr = inet_addr(HostIp);
hostAddr.sin_port = htons(HostPort);
if (connect(sock, (struct sockaddr *) &hostAddr, sizeof(hostAddr)) < 0) {
fprintf(stderr, "SendMail: connect() failed");
return;
}
read_socket();
send_socket(HELO);
read_socket();
send_socket("MAIL FROM: ");
send_socket(From);
send_socket("\r\n");
read_socket();
send_socket("VRFY ");
send_socket(From);
send_socket("\r\n");
read_socket();
send_socket("RCPT TO: ");
send_socket(To);
send_socket("\r\n");
read_socket();
send_socket(DATA);
send_socket("Subject: ");
send_socket(Subject);
send_socket("\r\n");
read_socket();
send_socket(Message);
send_socket("\r\n");
send_socket(".\r\n");
read_socket();
send_socket(QUIT);
read_socket();
closesocket(sock);
WSACleanup();
};
};
}

#endif


Usage:

#include "stdafx.h"
#include "SendMail.h"

int main(int argc, char* argv[])
{
mail::SendMail* sendmail = new mail::SendMail();
sendmail->HostIp = "192.168.1.10";
sendmail->From = "mail@address.com";
sendmail->To = "mail@address.com";
sendmail->Subject = "Test!";
sendmail->Message = "Content:\nTest 123...";
sendmail->Send();
return 0;
}

Friday, September 17, 2010

Force JPA load Identity ID after Insert

When you have an ID into your Entity Classe from Database and that is Identity, if you do an insert action the ID of this Entity isn't reloaded by default with the ID that was generated by Database, through JPA.

A sample of an ID of Identity kind:

@Id
@Basic(optional = false)
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name = "Id")
private Integer id;


To refresh the ID value with the ID that was generated by the Database on insert action, you need put more this below in your AbstractFacade.Create(EntityClass):

getEntityManager().flush();
getEntityManager().refresh(entity);


  • getEntityManager().flush() - Will do the commit to Database execute the insert action.

  • getEntityManager().refresh(entity) - Will refresh the entity object with a new ID that was inserted.


Full sample of the AbstractFacade.java:

package org.test.ejb;

import java.util.List;
import javax.persistence.EntityManager;

public abstract class AbstractFacade {
private Class entityClass;

public AbstractFacade(Class entityClass) {
this.entityClass = entityClass;
}

protected abstract EntityManager getEntityManager();

public void create(T entity) {
getEntityManager().persist(entity);
getEntityManager().flush();
getEntityManager().refresh(entity);
}

public void edit(T entity) {
getEntityManager().merge(entity);
}

public void remove(T entity) {
getEntityManager().remove(getEntityManager().merge(entity));
}

public T find(Object id) {
return getEntityManager().find(entityClass, id);
}

public List findAll() {
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
cq.select(cq.from(entityClass));
return getEntityManager().createQuery(cq).getResultList();
}

public List findRange(int[] range) {
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
cq.select(cq.from(entityClass));
javax.persistence.Query q = getEntityManager().createQuery(cq);
q.setMaxResults(range[1] - range[0]);
q.setFirstResult(range[0]);
return q.getResultList();
}

public int count() {
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
javax.persistence.criteria.Root rt = cq.from(entityClass);
cq.select(getEntityManager().getCriteriaBuilder().count(rt));
javax.persistence.Query q = getEntityManager().createQuery(cq);
return ((Long) q.getSingleResult()).intValue();
}
}


If you publish your JPA Create action in a Web Services, you will need return to client side an Entity instance to client know what is the new ID. Cause the Entity instance of the Client side can't be refresh with changes inside Server.
Like:

@WebMethod(operationName = "create")
public BusinessEntity create(@WebParam(name = "businessEntity")
BusinessEntity businessEntity) {
ejbRef.create(businessEntity);
return businessEntity;
}


Wednesday, September 15, 2010

Web Service - A cycle is detected in the object graph. This will cause infinitely deep XML...

When a Web Service that uses an EJB that exposes an Entity Class and this Entity Class makes relationships with another Entity Class, may be you will receives this error:

Caused by: javax.xml.bind.MarshalException
- with linked exception:
[com.sun.istack.SAXException2: A cycle is detected in the object graph. This will cause infinitely deep XML: org.test.data.Business[id=1] -> org.test.data.BusinessType[id=1] -> org.test.data.Business[id=1]]
at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:269)
at com.sun.xml.bind.v2.runtime.BridgeImpl.marshal(BridgeImpl.java:100)
at com.sun.xml.bind.api.Bridge.marshal(Bridge.java:141)
at com.sun.xml.ws.message.jaxb.JAXBMessage.writePayloadTo(JAXBMessage.java:317)
... 35 more
Caused by: com.sun.istack.SAXException2: A cycle is detected in the object graph. This will cause infinitely deep XML: org.test.data.Business[id=1] -> org.test.data.BusinessType[id=1] -> org.test.data.Business[id=1]
at com.sun.xml.bind.v2.runtime.XMLSerializer.reportError(XMLSerializer.java:248)
at com.sun.xml.bind.v2.runtime.XMLSerializer.pushObject(XMLSerializer.java:537)
at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:631)
at com.sun.xml.bind.v2.runtime.property.ArrayElementNodeProperty.serializeItem(ArrayElementNodeProperty.java:65)
at com.sun.xml.bind.v2.runtime.property.ArrayElementProperty.serializeListBody(ArrayElementProperty.java:168)
at com.sun.xml.bind.v2.runtime.property.ArrayERProperty.serializeBody(ArrayERProperty.java:155)
at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:340)
at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:696)
at com.sun.xml.bind.v2.runtime.property.SingleElementNodeProperty.serializeBody(SingleElementNodeProperty.java:152)
at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:340)
at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:696)
at com.sun.xml.bind.v2.runtime.property.ArrayElementNodeProperty.serializeItem(ArrayElementNodeProperty.java:65)
at com.sun.xml.bind.v2.runtime.property.ArrayElementProperty.serializeListBody(ArrayElementProperty.java:168)
at com.sun.xml.bind.v2.runtime.property.ArrayERProperty.serializeBody(ArrayERProperty.java:155)
at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:340)
at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:696)
at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:264)
... 38 more



To this error above you can found solutions saying to use this interface:
  • com.sun.xml.internal.bind.CycleRecoverable


NetBeans recognize this package and not mark like an error. But on compile time this package does not exist to compiler side. Perhaps you will receive this error when compiles:


/home/eduveks/project/wstest/src/org/test/data/Business.java:8: package com.sun.xml.internal.bind does not exist
import com.sun.xml.internal.bind.CycleRecoverable;


On my view this error is a bug of NetBeans that recognizes this package when isn't a valid package to Java compiler. To test this bug you only need put this "import com.sun.xml.internal.bind.CycleRecoverable;" into any Java file and you will see that NetBeans don't says nothing and accept as well, but if you "Clean and Build" the error will show up.

The correct package is (without .internal):


import com.sun.xml.bind.CycleRecoverable;


To uses that package path above you need add it:

Project Properties -> Libraries -> "Add Library...", select JAXB 2.2 and "Add Library".

A sample how to implements the com.sun.xml.bind.CycleRecoverable:

    > Business.java

package org.test.data;

import com.sun.xml.bind.CycleRecoverable;
import java.io.Serializable;
import java.util.Collection;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.xml.bind.annotation.XmlID;
import javax.xml.bind.annotation.XmlIDREF;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import org.test.data.adapter.IntegerAdapter;

@Entity
@Table(name = "Business")
@NamedQueries({
@NamedQuery(name = "Business.findAll", query = "SELECT b FROM Business b"),
@NamedQuery(name = "Business.findById", query = "SELECT b FROM Business b WHERE b.id = :id"),
@NamedQuery(name = "Business.findByNome", query = "SELECT b FROM Business b WHERE b.name = :name"),
public class Business implements Serializable, CycleRecoverable {
private static final long serialVersionUID = 1L;
@Id
@Basic(optional = false)
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name = "Id")
private Integer id;
@Basic(optional = false)
@Column(name = "Name")
private String name;
@JoinColumn(name = "BusinessTypeId", referencedColumnName = "Id")
@ManyToOne(optional = false)
private BusinessType businessType;

public Business() {
}

public Business(Integer id) {
this.id = id;
}

public Business(Integer id, String name) {
this.id = id;
this.name = name;
}

@XmlID
@XmlJavaTypeAdapter(value = IntegerAdapter.class, type = String.class)
public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@XmlIDREF
public BusinessType getBusinessType() {
return businessType;
}

public void setBusinessType(BusinessType businessType) {
this.businessType = businessType;
}

@Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}

@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof Business)) {
return false;
}
Business other = (Business) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}

@Override
public String toString() {
return "org.test.data.Business[id=" + id + "]";
}

@Override
public Object onCycleDetected(Context cntxt) {
System.out.println("CycleRecoverable.onCycleDetected # ".concat(this.toString()));
Business n = new Business();
n.setId(this.getId());
return n;
}
}


    > BusinessType.java

package org.test.data;

import com.sun.xml.bind.CycleRecoverable;
import java.io.Serializable;
import java.util.Collection;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.xml.bind.annotation.XmlID;
import javax.xml.bind.annotation.XmlIDREF;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import org.test.data.adapter.IntegerAdapter;

@Entity
@Table(name = "BusinessType")
@NamedQueries({
@NamedQuery(name = "BusinessType.findAll", query = "SELECT bt FROM BusinessType bt"),
@NamedQuery(name = "BusinessType.findById", query = "SELECT bt FROM BusinessType bt WHERE bt.id = :id"),
@NamedQuery(name = "BusinessType.findByName", query = "SELECT bt FROM BusinessType bt WHERE bt.name = :name")})
public class BusinessType implements Serializable, CycleRecoverable {
private static final long serialVersionUID = 1L;
@Id
@Basic(optional = false)
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name = "Id")
private Integer id;
@Basic(optional = false)
@Column(name = "Name")
private String name;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "BusinessType")
private Collection businessCollection;

public BusinessType() {
}

public BusinessType(Integer id) {
this.id = id;
}

public BusinessType(Integer id, String name) {
this.id = id;
this.name = name;
}

@XmlID
@XmlJavaTypeAdapter(value = IntegerAdapter.class, type = String.class)
public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@XmlIDREF
public Collection getBusinessCollection() {
return businessCollection;
}

public void setBusinessCollection(Collection businessCollection) {
this.businessCollection = businessCollection;
}

@Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}

@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof BusinessType)) {
return false;
}
BusinessType other = (BusinessType) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}

@Override
public String toString() {
return "org.test.data.BusinessType[id=" + id + "]";
}

@Override
public Object onCycleDetected(Context cntxt) {
System.out.println("CycleRecoverable.onCycleDetected # ".concat(this.toString()));
BusinessType n = new BusinessType();
n.setId(this.getId());
return n;
}
}


You need mark the IDs with @XmlID(only before the get method, before the variable declaration don't works):

@XmlID
@XmlJavaTypeAdapter(value = IntegerAdapter.class, type = String.class)
public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}


And mark all Collection relationships methods with @XmlIDREF(only before the get method, before the variable declaration don't works):

    > BusinessType.java

@XmlIDREF
public Collection getBusinessCollection() {
return businessCollection;
}

public void setBusinessCollection(Collection businessCollection) {
this.businessCollection = businessCollection;
}


Sample of the IntegerAdapter.java:

package org.test.data.adapter;

import javax.xml.bind.annotation.adapters.XmlAdapter;

public class IntegerAdapter extends XmlAdapter {

@Override
public Integer unmarshal(String v) {
return Integer.parseInt(v);
}

@Override
public String marshal(Integer v) {
return v.toString();
}
}



Thursday, July 29, 2010

Automate Command-line Programs

I had to automate a command-line program. Then I did this script like a sample that do auto control to vi.

This way is possible control any kind of command-line program.

#!/bin/python

import subprocess
import time
import threading

class ProcessRunner(threading.Thread):
def run(self):
self.process = subprocess.Popen(["vi /tmp/xpto > /tmp/viOutput"], shell = True, bufsize = 1000, stdout = subprocess.PIPE, stdin = subprocess.PIPE, stderr = subprocess.STDOUT)

pr = ProcessRunner()
pr.start()

time.sleep(1)

pr.process.stdin.write("i\n")
pr.process.stdin.flush()

time.sleep(1)

pr.process.stdin.write("Content xpto... :P\n")
pr.process.stdin.flush()

time.sleep(1)

pr.process.stdin.write(chr(27))
pr.process.stdin.flush()

time.sleep(1)

pr.process.stdin.write(":wq\n")
pr.process.stdin.flush()


If is necessary read some content from program output, you can put the output in a temp file, in this case goes to /tmp/viOutput. See with attention the line of the subprocess.Popen. Another way is use the pr.process.stdout but it dont work fine for me.

The command pr.process.stdin.write(chr(27)) write the "character" ESC, like the keyboard key.

Thursday, February 4, 2010

NC (NetCast) vs Printers (Impressoras)

Sobrou para mim configurar as novas impressoras Samsung SCX-6345N no Solaris, e como é óbvio e uma grande pena não tem drivers, ou pelo menos que funcione bem, pois apesar de haver um driver PPD supostamente para o LP em Solaris, não funcionou nem a pau.

Fiz tudo o possível para fazer funcionar estas impressoras em Solaris via LP, pois era uma situação de urgência fazer o sistema da Reuters (Kondor) utilizar as novas impressoras.

Após muitas tentativas e alguns e-mails trocados com o suporte da Samsung, e até um programa da Samsung para instalar a impressora em modo gráfico (X11) que "configuraria automaticamente" o LP, também não funcionou. E após também de muitas folhas de erros e cabeçalho de jobs terem sido impressos sem nunca sair o conteúdo desejado. Após tudo isto e um pouco mais... o suporte da Samsung recomendou eu usar o comando:

# cat testprint.ps | nc -w 2 192.168.193.191 9100


Fiquei sético, mas como não havia muito mais a fazer, lá fui eu experimentar isto, e para a minha surpresa... não é que funcionou!

Por acaso eu já tinha pensado nisto, em abrir uma conexão com a impressora direto e mandar o conteúdo PostScript e ver se ela imprimia ou se ficava maluca. Mas não sabia que existia um comando simples para isto.

O comando NC (NetCast) não vem no Solaris 10, por isso fui ao Sunfreeware.com pegar o pacote:

http://www.sunfreeware.com/programlistsparc10.html#nc

Depois é só instalar o pacote:

#pkgadd -d nc-110-sol10-sparc-local.gz


Na configuração da impressora no Kondor (Reuters) ficou apenas assim:

nc -w 2 192.168.193.191 9100


Portanto é executar o nc passando o IP e a PORTA da impressora. O -w 2 apenas quer dizer que tem o timeout de 2 segundos.

A desvantagem desta solução é que perde-se o controle das impressoras com o LP, ou seja, para o Solaris é como se elas não existissem. Então a administração LP e a configuração das impressoras no /etc/lp e os comandos ("lpstat -a", "enable PRINTER" e "disable PRINTER"), tudo isto passa a servir para nada! :D

Como para nos a solução do NC (NetCast) é suficiente, passamos a trabalhar assim.

NC (NetCast)

netcat is a simple unix utility which reads and writes data across network connections, using TCP or UDP protocol. It is designed to be a reliable "back-end" tool that can be used directly or easily driven by other programs and scripts. At the same time, it is a feature-rich network debugging and exploration tool, since it can create almost any kind of connection you would need and has several interesting built-in capabilities. Netcat, or "nc" as the actual program is named, should have been supplied long ago as another one of those cryptic but standard Unix tools.

http://www.computerhope.com/unix/nc.htm

Esta solução de imprimir diretamente para uma impressora de rede usando o NC (NetCast) também é válida para Linux e Unix em geral.

Tuesday, October 20, 2009

Solaris - PDF Printer

Precisei encarar este desafio de configurar uma impressora de PDF em Solaris, para receber a impressão de documentos em PostScript apartir do Kondor.

Encontrei este link que ajudou bastante:

http://aplawrence.com/SCOFAQ/FAQ_scotec7printtofile.html

Então os passos que fiz e cheguei ao sucesso são...

Criar a pasta que irá conter o script que receberá a os dados de impressão:

# mkdir /usr/local/PDFprinter


Criar uma outra pasta que irá conter o arquivos de PDF:

# mkdir /var/PDFprinter


Gerar este arquivo de script em /usr/local/PDFprinter/start que será usado para preparar o device da impressora e manter o device em sincronização com o script de impressão:

mknod /dev/PDFprinter p
chmod 777 /dev/PDFprinter
while true
do
cat /dev/PDFprinter | /usr/local/PDFprinter/print
done


Permissões de execussão:

# chmod +x /usr/local/PDFprinter/start


Gerar o script de impressão em /usr/local/PDFprinter/print:

date=`date "+%G%m%d_%H%M%S"`
tempFileName="/tmp/PDFprinter_$date"
while read stuff
do
echo $stuff >> $tempFileName
done
cat $tempFileName | grep -v "#####" > $tempFileName.tmp
mv $tempFileName.tmp $tempFileName
fileName="/var/PDFprinter/PDFprinter_$date.pdf"
ps2pdf -sPAPERSIZE=a4 $tempFileName $fileName


O comando ps2pdf faz parte do pacote ghostscript:

http://www.sunfreeware.com/programlistsparc10.html#ghostscript

E para definir o tamanho do papel é com o parâmetro ps2pdf -sPAPERSIZE=a4 ...

Permissões de execussão:

# chmod +x /usr/local/PDFprinter/print


Preparar e iniciar o device da impressora:

# /usr/local/PDFprinter/start &


Registrar o device da impressora e activar a impressora:

# lpadmin -p PDFPrinter -v /dev/PDFprinter
# accept PDFPrinter
# enable PDFPrinter


Agora é só mandar alguma coisa em PostScript para imprimir nesta impressora e ver o resultado em arquivos temporários em /tmp, e caso gere o PDF com sucesso deverá estar em /var/PDFprinter;

Monday, September 28, 2009

Process ID

Quando se faz um serviços Unix em Java para poder fazer os scripts de stop, kill, etc, é preciso o PID (número do processo), então para tal é preciso gerar um arquivo que contém esta informação.

Portanto este código faz exatamente isto, gera um arquivo com o PID da aplicação Java:

java.io.BufferedWriter bw = null;
try {
bw = new java.io.BufferedWriter(new java.io.OutputStreamWriter(new java.io.FileOutputStream("my.pid")));
String pidInfo = java.lang.management.ManagementFactory.getRuntimeMXBean().getName();
bw.write(pidInfo.substring(0, pidInfo.indexOf('@')));
} finally {
if (bw != null) bw.close();
}

Friday, June 19, 2009

Jetty e Web Services com JAX-WS

O Jetty é um poderoso servidor web, disto não há dúvidas, mas no que toca a web services paira uma nuvem negra, não há muita documentação e nem integração com IDEs.

Depois de pesquisar um pouco vi que era possível integrar com o JAX-WS.

Não encontrei nenhuma explicação detalhada de como fazer, e que explicasse também a parte do cliente, então resolvi fazer o download do JAX-WS e ver os exemplos e a documentação, e a conclusão que cheguei segue abaixo.

Levando em consideração que já tem o Jetty configurado e rodando, no meu caso foi a versão 6 e com o JDK 6.

Fazer o download do JAX-WS no meu caso foi a versão 2.1.7 em:

https://jax-ws.dev.java.net/

Descompactar e em jaxws-ri/docs/UsersGuide.html#3.1_Server da para ver alguma explicação de como fazer.

Copiar todos os jars que estão na pasta lib do JAX-WS para a pasta lib/ext do Jetty.

Agora no seu projeto web criar as seguintes classes de exemplo (também estão em jaxws-ri/samples/fromjava/src), aqui dei alguma melhoria nas anotações:

-> fromjava/server/AddNumbersImpl.java:

package fromjava.server;
import javax.jws.WebService;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.handler.MessageContext;
import javax.annotation.Resource;
@WebService
public class AddNumbersImpl {
@Resource
private WebServiceContext context;
@WebMethod(operationName = "add")
public int addNumbers(@WebParam(name = "first") int number1, @WebParam(name = "second") int number2) throws AddNumbersException {
// SERVLET - IF NEED SERVLET RESOURCES. For example to do session control.
MessageContext messageContext = context.getMessageContext();
ServletContext servletContext = (ServletContext)messageContext.get(MessageContext.SERVLET_CONTEXT);
HttpServletRequest servletRequest = (HttpServletRequest)messageContext.get(MessageContext.SERVLET_REQUEST);
HttpServletResponse servletResponse = (HttpServletResponse)messageContext.get(MessageContext.SERVLET_RESPONSE);
// --
if (number1 < 0 || number2 < 0) {
throw new AddNumbersException("Negative number cant be added!",
"Numbers: " + number1 + ", " + number2);
}
return number1 + number2;
}
}

-> fromjava/server/AddNumbersException.java:

package fromjava.server;
public class AddNumbersException extends Exception {
String detail;
public AddNumbersException (String message, String detail) {
super (message);
this.detail = detail;
}
public String getDetail () {
return detail;
}
}

Acrescentar no web.xml:

<listener>
<listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener</listener-class>
</listener>
<servlet>
<description>JAX-WS endpoint - fromjava</description>
<display-name>fromjava</display-name>
<servlet-name>fromjava</servlet-name>
<servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>fromjava</servlet-name>
<url-pattern>/addnumbers</url-pattern>
</servlet-mapping>

E junto com o web.xml criar o arquivo sun-jaxws.xml:

<?xml version="1.0" encoding="UTF-8"?>
<endpoints xmlns='http://java.sun.com/xml/ns/jax-ws/ri/runtime' version='2.0'>
<endpoint
name='fromjava'
implementation='fromjava.server.AddNumbersImpl'
url-pattern='/addnumbers'/>
</endpoints>

Feito isto, é rodar o Jetty e ver o WSDL:

http://localhost:8080/WSTester/addnumbers?WSDL

Onde o WSTester é o nome do seu projeto.

Agora temos que criar as classes para chamar o web service, para isto o JAX-WS trás o bin/wsimport, no meu caso como estou no Linux vou usar o .sh, caso esteja no Windows é só usar o .bat, criar as pastas src e dest dentro da pasta do JAX-WS.

$ chmod +x bin/wsimport.sh
$ mkdir dest
$ mkdir src
$ bin/wsimport.sh -d dest -s src http://localhost:8080/WSTester/addnumbers?WSDL

O wsimport vai gerar o código das classes para invocar o web service na pasta src, e as classes compiladas em dest.

Eu peguei na pasta src/fromjava e copiei para o um outro projeto de aplicação do tipo consola em Java. Assim pode chamar o web service:

TestAddNumbers.java:

package javaapplicationWSTester;
import fromjava.server.*;
public class TestAddNumbers {
public static void main(String[] args) throws AddNumbersException_Exception {
int result = new AddNumbersImplService().getAddNumbersImplPort().add(3, 5);
System.out.println(result);
}
}

O processo é simples, ter a classe do web service, configurar no web.xml e no sun-jaxws.xml, gerar as classes de invocação com o wsimport e boa :P

Monday, May 18, 2009

SUDO + PHP - Executando comandos como Root no PHP

É preciso saber qual é o usuário de execução do PHP:

<?php echo exec("whoami"); ?>


Editar o /etc/sudoers, este arquivo tem que ter permissões apenas de leitura, não pode ficar com permissões de escrita, por isso antes de editar o arquivo dê permissões de escrita:

# chmod u+w /etc/sudoers


Depois de editar, tire a permissão de escrita:

# chmod u-w /etc/sudoers


Então adicionar no /etc/sudoers no fim do arquivo:

%USUARIO_DO_PHP ALL= NOPASSWD: /CAMINHO/DO/COMANDO


Se não colocar no fim do arquivo pode ter outra regra depois que anule esta nova regra.

No PHP é só executar o comando com o SUDO:

<?php echo exec("sudo /CAMINHO/DO/COMANDO.sh"); ?>


Caso não tenha sucesso, verifique os logs de segurança:

# tail -n 100 /var/log/secure


Se tiver esta mensagem de erro:

May 18 16:56:41 SERVIDOR sudo: USUARIO_DO_PHP : sorry, you must have a tty to run sudo ; TTY=unknown ; PWD=/PASTA/DO/PHP ; USER=root ; COMMAND=/CAMINHO/DO/COMANDO


Então é preciso ir no /etc/sudoers e comentar a seguinte linha que contém o "Defaults requiretty", assim:

# Defaults requiretty


Assim deverá funcionar.

Friday, March 13, 2009

Ubuntu, iniciando o Compiz & NVIDIA com Antialiasing

Para por configurar o Compiz no arranque do Gnome, vá em:

System -> Preferências -> Sessões


E adicione o Compiz:

Nome: Compiz
Comando: compiz --replace &
Comentário: Desktop 3D


Fica assim o Compiz configurado para sempre iniciar no arranque do Gnome.

Depois de configurar a qualidade de antialiasing e outras configurações em:

Systema -> Administração -> NVIDIA X Server Settings

Fazer as devidas configurações, principalmente a do "Antialiasing Settings".

Para que o Gnome carregue as configurações do driver no arranque da sessão, edite o arquivo:

/usr/share/xsessions/gnome.desktop


A linha do parâmetro "Exec", para:

Exec="nvidia-settings -l && /usr/bin/gnome-session"


Assim sempre que o Gnome iniciar ele vai aplicar as configurações do driver e rodar o Compiz.

Thursday, February 5, 2009

Erro do AJAX .Net no Internet Explorer, comédia!

Bem que o IE é uma porcaria todo mundo sabe. Tirando a lentidão do coitado ainda é cheio de bugs, e cá vai mais um.

Em aplicações .Net ASPX com AJAX, pode acontecer de algum sortudo ter o seguinte erro de JavaScript após algum tempo de execução:

Sys.WebForms.PageRequestManagerParserErrorException: The message received from the server could not be parsed. Common causes for this error are when the response is modified by calls to Response.Write(), response filters, HttpModules, or server trace is enabled.
Details: Error parsing near '


E o melhor deste erro é que só acontece no IE, no Firefox funciona sempre perfeitamente. Basicamente um problema exclusivo do IE, ainda não sei por que não colocam o Firefox nas empresas, a desculpa de que não é corporativo não cola.

Para resolver isto a "melhor" maneira para o usuário é, limpar os arquivos temporários e reiniciar o browser :D

Andei procurando a algumas semanas uma solução para isto e a única que me ajudou foi neste link, lá em baixo:

http://weblogs.asp.net/leftslipper/archive/2007/02/26/sys-webforms-pagerequestmanagerparsererrorexception-what-it-is-and-how-to-avoid-it.aspx#6815054

# re: Sys.WebForms.PageRequestManagerParserErrorException - what it is and how to avoid it
Friday, January 02, 2009 1:20 PM by Arjan Douwes
I am experiencing the same issue with my Ajax.net website. After adding <customErrors mode="Off"/> to the web.config file at the problem seems to be that the site is being hosted on a webfarm and the SessionState is being encrypted using the MAC Address. The issue with webfarms is that the EnableViewStateMac is by default set to True which causes problems.
In the web.config I changed the <Page> tag to <Page EnableViewStateMac="False">
I also added
EnableEventValidation="false" EnableViewStateMac="false"
to the <%@ Page ... directive of the aspx pages.
These two changes to the website solved my issue.


Ou seja, mudar no web.config o customErrors:


<customErrors mode="Off"/>


E colocar nos ASPX que trabalham com AJAX:


<%@ Page Language="C#" EnableEventValidation="false" EnableViewStateMac="false" ...


Pronto, no meu caso resolveu, e se tiveres sorte também poderá resolver.

Wednesday, February 4, 2009

Compilando a última versão do WINE no Ubuntu

Para quem não sabe, o WINE permite rodar aplicações Windows em Linux/BSDs sem precisar de uma cópia do Windows. Basicamente, com ele é possível rodar a maioria dos programas desenvolvidos para Windows no Linux.

WINE quer dizer: Wine Is Not an Emulator! O WINE não é um emulador por que ele não emula a arquitetura de processadores do Windows, o Intel x86, ele simplismente consegue executar os binários do Windows na arquitetura em que esta rodando, como uma ponte entre os binários do Windows e o sistema nativa em que ele esta rodando (Linux/BSDs) independente da arquitetura do processador.

O Ubuntu tem o WINE disponível no repositório oficial, mas nunca é a versão mais resente, e como estão sempre melhorando para rodar melhor os programas Windows, convém mante-lo atualizado.

O WINE pode consumir bastante recursos da máquina, dependendo das aplicações que ele vai rodar (jogos por exemplo).

Por estas e outras o melhor é estar sempre com a versão mais recente e compilado para ter melhor desempenho.

Antes de mais, instalar as dependências do WINE:


$ sudo apt-get build-dep wine


Mas estas dependências não são completas, por isso veja em:

http://wiki.winehq.org/Recommended_Packages

E baixe e execute o arquivo de script de dependências correspondente a sua versão do Ubuntu, no meu caso:


$ wget http://winezeug.googlecode.com/svn/trunk/install-wine-deps.sh
$ chmod +x install-wine-deps.sh
$ sudo ./install-wine-deps.sh


Fazer o download da última versão do arquivo de source (.tar.bz2) do WINE em:

http://www.winehq.org/

Extrair o arquivo baixado. Através da console entrar dentro da pasta de sources do WINE e fazer:


$ ./configure


Se no final do "./configure" obtiver o seguinte warning:

configure: WARNING: No OpenGL library found on this system.
OpenGL and Direct3D won't be supported.


Então os links simbólicos da libGL não devem estar corretas, analise com:


$ ls /usr/lib/libGL* -o


É preciso que o link simbólico /usr/lib/libGL.so -> /usr/lib/libGL.so.XXX.XX esteja a apontar para a versão mais recente, onde "XXX.XX" é a identificação da versão mais alta, ou seja a mais recente, da libGL. Caso exista o link simbólico remova-o:


$ sudo rm /usr/lib/libGL.so


E agora crie o link simbólico para a versão mais recente que tiver:


$ sudo ln -s /usr/lib/libGL.so.XXX.XX /usr/lib/libGL.so


Tem que ser feito o ./configure outra vez, e agora sem o warning:


$ ./configure


Dependências e configurações feitas com sucesso, agora só falta compilar e instalar:


$ make depend && make
$ sudo make install


Pronto! Será criado menus para o WINE no menu de programas, e através da console é só executar:


$ wine ARQUIVO_EXECUTAVEL_DO_WINDOWS.exe


Para ver a lista dos programas suportados e a nível da qualidade de execução:

http://appdb.winehq.org/

Tuesday, February 3, 2009

Virtualização para servidores com VirtualBox

O que não falta agora é soluções de virtualização.

Mas a mais simples e com excelente performance na minha opinião é com o VirtualBox, dá para fazer tudo que é preciso com muita simplicidade e rapidez.

Para virtualização em desktop o VirtualBox já vem reinando a algum tempo.

E para servidor, muitos não apostam por que simplismente não sabem que é possível, mas dá! E não é preciso ter ambiente gráfico, usando o VRDP pode-se administrar a máquina remotamente. Mas convém ter o ambiente gráfico, tudo fica muito mais fácil, e hoje em dia não vejo por que não ter ambiente gráfico no servidor, pois o que não falta é ambientes gráficos levezinhos que nem afetam a performance do servidor.

Existe uma solução para máquinas virtuais usando OpenSolaris e VirtualBox, chamda xVM Server:
http://xvmserver.org/
http://www.sun.com/software/products/xvmserver/index.xml

Mas como já tenho o CentOS redondinho, agora é tarde.

Bem, então eu resolvi fazer de tudo para ter o VirtualBox trabalhando no servidor com o Windows 2003 Server em máquina virtual dentro do VirtualBox no CentOS 5.

Melhor impossível, ficou perfeito, excelente performance, fácil administração, todos serviços funcionando lindamente.

Vamos ao que interessa. Basicamente CentOS 5 com o VirtualBox instalado, com a máquina virtual para o Windows Server 2003 instalada e configurada, isto é moleza e o que não falta é tutoriais pela net ensinando como fazer isto com screenshots e tudo, e o VirtualBox é muito intuitivo. Por isso este não é o âmbito aqui.

Agora a parte mais complicada, que tive que pesquisar bastante, para fazer com que o CentOS comunicasse com o Windows Server. O que me ajudou mais a desvendar isto foi este script:

http://www.savvyadmin.com/virtualbox-host-interface-networking-with-nat/

Mas como nem tudo é perfeito, este script não serve no CentOS, é preciso algumas alterações.

Então em primeiro lugar, configurar o IP da máquina virtual, ir no Windows 2003 Server e configurar o TCP/IP assim:

IP address: 192.168.20.201
Subnet mask: 255.255.255.0
Default gateway: 192.168.20.1



Feito isto, ir no CentOS e criar uma interface de rede virtual:


# /usr/bin/VBoxTunctl -u root


Depois disto, pode verificar com o "/sbin/ifconfig -a" se aparece o tap0. Pronto ai esta a nossa interface de rede virtual.

É preciso configurar no VirtualBox para usar a nova interface de rede virtual, a tap0. Ir nos "Settings" da máquina virtual, em "Network", no "Attached to", mudar para "Host Interface", e selecionar a tap0. Para ir nos "Settings" tem que desligar a máquina virtual.

Settings da Máquina Virtual > Network > Attached to > Host Interface



Agora é preciso configurar a interface de rede virtual:


# /sbin/ip link set tap0 up
# /sbin/ip addr add 192.168.20.1/32 dev tap0
# /sbin/route add -host 192.168.20.201 dev tap0


Deverá conseguir "pingar" a máquina virtual:


# ping -c 1 192.168.20.201


Se for preciso rodar ASPX no servidor, ai esta a solução, só configuar no nginx para fazer proxy reverso do IIS no IP 192.168.20.201, e já vai funcionar perfeitamente.

A máquina virtual até aqui não tem acesso a internet. Para resolver isto é preciso fazer isto:


# /sbin/sysctl net.ipv4.ip_forward=1
# /sbin/iptables -t nat -A POSTROUTING --out-interface eth0 -j MASQUERADE
# /sbin/iptables -A FORWARD --in-interface tap0 -j ACCEPT


E também configurar os DNSs, primário e secundário, da máquina virtual, com os mesmos dados DNSs do CentOS, para ver os DNSs do CentOS:


# cat /etc/resolv.conf


Agora deverá ter a internet funcionando no Windows.

Podemos iniciar a máquina virtual pela linha de comando, só que ficamos sem acesso ao ambiente gráfico do VirtualBox. Para então ter acesso a máquina virtual convém configurar o VRDP no VirtualBox para acessar a máquina virtual remotamente:

Settings da Máquina Virtual > Remote Display > Enable VRDP Server

Settings da Máquina Virtual > Remote Display > Port = 3089



É importante mudar o número da porta, por que a porta do VRDP é a mesma do Terminal Services, da para trocar o número da porta do Terminal Services no regedit, mas é mais simples ai no VRDP, e assim já se evita um conflito.

Só que assim o VirtualBox vai deixar entrar qualquer um pelo VRDP sem autenticação, então convém bloquear a porta do VRDP para que ninguem fora do CentOS consiga controlar a máquina virtual:


# /sbin/iptables -A INPUT -p tcp -i eth0 --dport 3089 -j REJECT --reject-with tcp-reset

# /sbin/iptables -A INPUT -p tcp -i eth1 --dport 3089 -j REJECT --reject-with tcp-reset

# /sbin/iptables -A INPUT -p udp -i eth0 --dport 3089 -j REJECT

# /sbin/iptables -A INPUT -p udp -i eth1 --dport 3089 -j REJECT


Assim a porta do VRDP fica protegida para que ninguem que venha das interfaces de rede externas consiga conectar.

Para conectar via VRDP é só usar o tsclient:


# tsclient


No tsclient em computer colocar:

localhost:3089

E em protocol:

RDPv5

Clicar em "Connect", e pronto! Já conseguimos controlar a máquina virtual remotamente quando estivermos executando ela em background, isto será muito útil.

No Windows 2003 Server podemos ter o Terminal Services, e para acessar o terminal services através da internet ou rede temos que fazer uns ajustes, pois a porta do Terminal Services é a mesma do VRDP, então temos que mudar uma ou outra porta, como já mudamos a porta padrão do VRDP para 3089, então o Terminal Services vai funcionar bem na porta 3389.

Então para por o Terminal Services a funcionar é preciso ativar, no Windows Server:

Control Panel > System > Remote > Enable Remote Desktop on this computer



E agora é fazer NAT para a porta do Terminal Services:


# /sbin/iptables -t nat -A PREROUTING -p tcp -i eth0 --dport 3389 -j DNAT --to 192.168.20.201:3389

# /sbin/iptables -t nat -A PREROUTING -p tcp -i eth1 --dport 3389 -j DNAT --to 192.168.20.201:3389


E assim fica o Terminal Services disponível, e o mesmo pode ser feito para outros serviços do Windows que precisam estar disponíveis externamente, como MSSQLServer.

Há outros exemplos desta configuração usando bridges, mas acho que assim fica muito mais simples.

Para iniciar a máquina virtual pela linha de comando é assim:


/usr/bin/VBoxVRDP -startvm NOME_DA_MAQUINA_VIRTUAL_NO_VIRTUALBOX


Para quando o servidor iniciar, iniciar também a máquina virtual e carregar toda configuração, coloquei os seguintes comandos no /etc/rc.local:


/usr/bin/VBoxTunctl -u root
/sbin/sysctl net.ipv4.ip_forward=1
/sbin/ip link set tap0 up
/sbin/ip addr add 192.168.20.1/32 dev tap0
/sbin/route add -host 192.168.20.201 dev tap0
/sbin/sysctl net.ipv4.ip_forward=1
/sbin/iptables -t nat -A POSTROUTING --out-interface eth0 -j MASQUERADE
/sbin/iptables -A FORWARD --in-interface tap0 -j ACCEPT
/sbin/iptables -A INPUT -p tcp -i eth0 --dport 3089 -j REJECT --reject-with tcp-reset
/sbin/iptables -A INPUT -p tcp -i eth1 --dport 3089 -j REJECT --reject-with tcp-reset
/sbin/iptables -A INPUT -p udp -i eth0 --dport 3089 -j REJECT
/sbin/iptables -A INPUT -p udp -i eth1 --dport 3089 -j REJECT
/sbin/iptables -t nat -A PREROUTING -p tcp -i eth0 --dport 3389 -j DNAT --to 192.168.20.201:3389
/usr/bin/nohup /usr/bin/VBoxVRDP -startvm winserver > /dev/null &


Tem muita gente que acha deselegante usar o rc.local, mas eu não acho deselegante, muito pelo contrário, afinal pra que que ele serve? :P

No meu caso, em um AMD Opteron Quad-Core com 4GB de RAM, o Windows 2003 Server em máquina virtual de 512MB com o VirtualBox demora 6 segundos para estar completamente inicializado.

Wednesday, December 17, 2008

Chamando Web Services sobre HTTPS

Utilizar web service sobre HTTPS é da mesma forma que um web service HTTP.

Adicionar a referência web ao projeto, com a url HTTPS.

O uso do web service a nível de código é a mesma coisa.

Só que ao tentar chamar o web service poderá acontecer de obter esta mensagem erro:

The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.

Colocando este código antes da chamada ao web sersvice provavelmente irá resolver o problema:


System.Net.ServicePointManager.ServerCertificateValidationCallback = delegate
{
return true;
};


Basicamente é por que o .Net esta rejeitando o certificado por não ser um certificado válido, com o código acima, mesmo não sendo um certificado válido ele irá aceitar o certificado, e conseguirá comunicar com o web service.

Thursday, November 27, 2008

Script Python para consumir Web Services em .Net

Tive um desafio a poucos dias. Que foi consumir Web Services feitos em .Net a partir de um servidor Solaris 10 via processo.

De todas as soluções possíveis que estive a ver, a melhor foi usar Python.

Encontrei este exemplo:

http://users.skynet.be/pascalbotte/rcx-ws-doc/postxmlpython.htm

Assim fiz o WebServiceGenericClientNet.py:

#!/usr/bin/python

# by eduveks

import sys, httplib, array

try:
    host = ""
    url = ""
    namespace = ""
    header = ""
    method = ""
    params = {}

    for arg in sys.argv[1:]:
        if host == "" and arg.find("host=") == 0:
            host = arg[len("host="):]
        elif url  == "" and arg.find("url=") == 0:
            url  = arg[len("url="):]
        elif namespace == "" and arg.find("namespace=") == 0:
            namespace = arg[len("namespace="):]
        elif header == "" and arg.find("header=") == 0:
            header = arg[len("header="):]
        elif method == "" and arg.find("method=") == 0:
            method = arg[len("method="):]
        elif arg.find("param_") == 0:
            params[arg[len("param_"):arg.find("=")]] = arg[arg.find("=") + 1:]

    SOAP_Request = ""
    SOAP_Request += "<soap:Envelope"
    SOAP_Request += " soap:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\""
    SOAP_Request += " xmlns:xsi=\"http://www.w3.org/1999/XMLSchema-instance\""
    SOAP_Request += " xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""
    SOAP_Request += " xmlns:xsd=\"http://www.w3.org/1999/XMLSchema\">"
    if header != "":
        SOAP_Request += "<soap:Header>"
        SOAP_Request += header
        SOAP_Request += "</soap:Header>"
    SOAP_Request += "<soap:Body>"
    SOAP_Request += "<"+ method +" xmlns=\""+ namespace +"\">"
    for param in params.keys():
        SOAP_Request += "<"+ param +">"+ params[param] +"</"+ param +">"
    SOAP_Request += "</"+ method +">"
    SOAP_Request += "</soap:Body>"
    SOAP_Request += "</soap:Envelope>"

    http = httplib.HTTP(host)
    http.putrequest("POST", url)
    http.putheader("Host", host)
    http.putheader("User-Agent", "Python")
    http.putheader("Content-type", "text/xml; charset=\"UTF-8\"")
    http.putheader("Content-length", "%d" % len(SOAP_Request))
    http.putheader("SOAPAction", "\""+ namespace + method +"\"")
    http.endheaders()
    http.send(SOAP_Request)

    http_response_statuscode, http_response_statusmessage, http_response_header = http.getreply()
    SOAP_Response = http.getfile().read()
    if http_response_statuscode == 200 and http_response_statusmessage == "OK":
        print SOAP_Response[SOAP_Response.find("<"+ method +"Result>") + len("<"+ method +"Result>"):SOAP_Response.find("</"+ method +"Result>")]
    else:
        print "### ERROR ###############"
        if SOAP_Response.find("<faultstring>") > -1:
            print SOAP_Response[SOAP_Response.find("<faultstring>")  + len("<faultstring>"):SOAP_Response.find("</faultstring>")]
        print "Response: ", http_response_statuscode, http_response_statusmessage
        print http_response_header
        print SOAP_Response

except:
    print "### ERROR ###############"
    for err in sys.exc_info():
        print err


Para executar o script basta dar permissões de execução e executar:

chmod +x WebServiceGenericClientNet.py
./WebServiceGenericClientNet.py host=localhost url=/HelloWorld.asmx namespace=http://tempuri.org/ method=HelloWorld param_strSay=HelloWorld


O output é:

HelloWorld


Os parâmetros do WebServiceGenericClientNet.py são:

host: Endereço do servidor
url: Endereço do web service
namespace: Namespace do web service, que pode ser encontrado vendo o WSDL, http://localhost/HelloWorld.asmx?WSDL
method: Método do web service a ser chamado
param_*NOME_DO_PARAMETRO*: Caso o método precise de parâmetros, podem ser definidos assim, atenção a ordem.

Além de usar este script no Solaris, também acabei por usar este script para chamar web services apartir do .Net/C#, pois são web services que serão atualizados constantemente, com novos métodos e talz, e em vez de ficar atualizando o WSDL, basta chamar diretamente com o script a queima-roupa, e a configuração da chamada dos web services pode ser feita a partir de configurações em base de dados. Para fazer isto no .Net/C# foi só lançar um processo assim:

System.Diagnostics.Process process = new System.Diagnostics.Process();
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.FileName = "C:\Python26\python.exe";
process.StartInfo.Arguments = "WebServiceGenericClientNet.py host=localhost url=/HelloWorld.asmx namespace=http://tempuri.org/ method=HelloWorld param_strSay=HelloWorld";
process.StartInfo.WorkingDirectory = "C:\Python26\";
process.Start();
string output = process.StandardOutput.ReadToEnd();

O script suporta a configuração do header, passando o argumento header=... para o script.

Para explorar e ver fácilmente os SOAPs enviados e recebidos na chamada de um web service, existe o WebServiceStudio: