Archivo de la categoría ‘Nivel Medio’

Creación de una plantilla para proyecto con Maven

Sábado, 9 de abril de 2011

Como ya hemos dicho, Maven es una herramienta para compilar y gestionar proyectos.  Es muy usada en proyectos Java, aunque es genérica y podría ser usada para cualquier lenguaje. Está basada en que el proyecto tiene una estructura de directorios específica que veremos posteriormente, aunque es totalmente configurable.

Creación de una plantilla vacía.

Una plantilla para Maven tiene la siguiente estructura:

archetype
|-- pom.xml
`-- src
    `-- main
        `-- resources
            |-- META-INF
            |   `-- maven
            |       `--archetype.xml ó archetype-metadata.xml
            `-- archetype-resources
                |-- pom.xml
                `-- src
                    |-- main
                    |   `-- java
                    `-- test
                        `-- java

Como puedes ver, sigue la estructura de cualquier proyecto Maven. Hay varios ficheros que hay que tener en cuenta:

  • pom.xml: En el directorio raíz se encuentra el fichero POM con la información para gestionar el proyecto para generar la plantilla.
  • pom.xml: Será el POM del proyecto que se genere usando la plantilla que estamos creando. Podemos decir que el contenido de la carpeta archetype-resources será el contenido del proyecto que generemos con esta platilla.
  • archetype.xml ó archetype-metadata.xml: Describe los ficheros que contendrá el proyecto que se genere con esta plantilla. El primero (archetype.xml) es para la versión 1 del plugin, mientras que el segundo (archetype-metadata.xml) se usa en la versión 2 o superiores.

En el directorio archetype-resources estarán los ficheros que contendrá el proyecto que generemos con esta plantilla.

Puedes crear esa estructura a mano, pero es más sencillo usar el plugin Archetype (página del plugin Archetype) que te permite crear esa estructura sin esfuerzo. Para esto usaremos el siguiente comando:

mvn archetype:generate

Creación de una plantilla basándose en un proyecto existente.

Si ya tienes un proyecto sobre el que quieras crear un modelo para poder generar proyectos similares puedes ejecutar el siguiente comando:

mvn archetype:create-from-project

Una vez que Maven haya finalizado su ejecución, en la carpeta target/generated-source tendrás la plantilla para tu proyecto con los ficheros pom.xml, archetype.xml y archetype-metadata.xml generados de forma automática.

Es posible que necesites realizar alguna modificación en los ficheros para adaptar mejor la plantilla a tus necesidades. Por ejemplo:

  • Organizar el código en un paquete
  • Definir variables en el contenido del fichero para adaptar ciertos nombres a tus necesidades.
  • Personalizar los nombres de ficheros según unas variables.

Organizar los ficheros en paquetes

Dentro del fichero archetype-metadata.xml encontraremos una serie de descriptores de los directorios en los que se compone nuestro proyecto. Un ejemplo sería:

<fileSet filtered="true" packaged="true" encoding="UTF-8">
   <directory>src/main/java</directory>
   <includes>
      <include>**/*.java</include>
   </includes>
</fileSet>

Como vemos es bastante descriptivo, pero vamos a explicar la parte que nos interesa.  En el elemento fileSet vemos el atributo packaged="true", mediante este atributo indicamos a maven que al prosesar el contenido de ese directorio use una estructura de paquetes. Si por ejemplo, el contenido de ese directorio es

src/main/java
          |-- common
          |    `-- Objeto.java
          |-- servicio
                `-- Objeto.java

en el proyecto resultado estará dentro de una estructura de paquetes indicado mediante la variable ${package}. Si por ejemplo el contenido de esta variable es es.excelsit.miProyecto el resultado sería:

src/main/java
    es
    `--excelsit
        `-- miProyecto
              |-- common
              |   `-- Objeto1.java
              |-- servicio
                  `-- Objeto2.java

El fichero para la clase Objeto1 y Objeto2 deberá contener la instrucción package es.excelsit.miProyecto;. Para ello usaremos la siguiente sintaxis para la definición del paquete:

package ${package};

Uso de variables en la plantilla.

Si abrimos alguno de los ficheros de nuestra plantilla veremos que todos comienzan con algo asi:

#set ( $symbol_pound = '#' )
#set ( $symbol_dollar = '$' )
#set ( $symbol_escape = '\' )

Como vemos por su sintaxis, el plugin usa de forma interna Velocity para la generación y adaptación de los ficheros de nuestra platilla de proyecto. Esto nos permite mucha libertad a la hora de crear el contenido del fichero. Hay que tener en cuenta que como  los símbolos almoadilla (#), dolar ($) y barra invertida (\) son caracteres especiales, si los queremos usar deberemos hacerlo mediante sus variables $symbol_pound, $symbol_dollar y $symbol_escape correspondientes.

Añadir propiedades propias.

Podremos crear propiedades propias de la siguiente forma:

#set ( $miPropiedad = 'valor' )

En este punto tendremos toda la libertad que ofrece Velocity para concatenar cadenas, obtener la fecha del sistema, etc. Estas propiedades que creemos las podemos usar dentro del contenido del fichero mediante:

$miPropiedad

También tenemos la posibilidad de renombrar ficheros según nuestras necesidades durante el proceso de generación de nuestro proyecto a partir de la plantilla. Para poder usar variables en el nombre del fichero usaremos dos careateres guión bajo (_) rodeando el nombre de la varible para indicar que se debe sustituir por su vlaor. Por ejemplo:

__miPropiedad__App.java
__artifactId__-context.xml
__groupId__App.java

Personalizar el nombre de ficheros con variables.

De forma predeterminada tenemos usa serie da variables ya predefinidas quepodemos usar para renombrar ficheros de nuestra plantilla, pero nos puede interesar crear variables en el momento de generar el proyecto para renombrar ficheros. Para poder hacer esto podemos valernos de que el primer fichero que se procesa es el pom.xml y crear en el las variebles con el valor que necesitemos. Posteriormente podremos hacer referencia a esas variables definidas en el nombre del fichero. Por ejemplo podemos definir el fichero pom.xml de la siguiente forma:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#set ( $customProperty = 'Excelsit' )
#set ( $theArtifactId = $artifactId )
#set ( $thegroupId = $groupId )
#set ( $theVersion = $version )
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>$thegroupId</groupId>
 <artifactId>$theArtifactId</artifactId>
 <version>$theVersion</version>
 <dependencies>
 <dependency>
 <groupId>junit</groupId>
 <artifactId>junit</artifactId>
 <version>3.8.1</version>
 <scope>test</scope>
 </dependency>
 </dependencies>
</project>

En la línea 1 hemos definido una propiedad a la que le hemos dado cierto valor. Pero recuerda que internamente funciona con Velocity, por lo que podremos generar ese valor de cualquier forma. Podremos usar esa propiedad para renombrar ficheros según la forma que hemos visto anteriormente __customProperty_App.java

Usar la plantilla.

Para usar la plantilla lo más sencillo es realizar una instalación local. Usaremos el siguiente comando dentro de la carpeta de nuestro proyecto:

$mvn install

Una vez instalado podremos usarlo meniante archetype:generate. Por defecto el plugin buscará en catalogo central de Maven. Para modificar este comportamiento usaremos el parámetro archetypeCatalog=local:

mvn archetype:generate -DarchetypeCatalog=local

Si no aparece nuestra plantilla quizás deberamos actualizar el catalogo mediante el comando:

mvn archetype:crawl

Código de ejemplo

Descarga el ejemplo de plantilla: excelsit-example-archetype.

Busquedas para encontrar este articulo.

como crear un archetype de maven

maven creacion archetype

maven archetype

Etiquetas: , , , ,

Uso del Table Control en tu Dynpro

Miércoles, 11 de agosto de 2010

Para usar correctamente este control hay que tener un punto clave muy claro. En cada ciclo PAI – PBO el Table Control mostrara el contenido de la tabla interna al que se haya asociado. Por lo tanto, si se hace una modificación en los datos que se están mostrando en la Dynpro estos hay que salvarlos en la tabla interna en el PAI para que al completarse el ciclo se muestren las modificaciones.

Otra de las necesidades más comunes al usar un Table Control es el hecho de activar/desactivar alguna de sus celdas según una condición en alguno de los campos de la fila en la que se encuentra o según el contenido de ese campo. Esta tarea se realiza en el PBO ya que es algo que se debe realizar justo antes de mostrar la tabla.

En el siguiente esquema vemos cada una de las acciones que se pueden realizar para manejar un Table Control y donde se deben realizar.

Esquema general

Pasos previos para crear un Table Control.

Crear un Table Control es un proceso muy sencillo usando el Wizard que tenemos en el entorno de desarrollo, pero antes de ejecutar este Wizard hay que crear una serie de elementos en nuestro código:

  1. Crear un tipo estructura que contendrá los campos que se mostrarán en la tabla junto con un campo oculto que se usará de forma interna para marcar las filas seleccionadas. Para nuestro ejemplo quedará de la siguiente forma, donde el campo mark será el campo interno para marcar las filas seleccionadas:
1
2
3
4
5
6
7
8
9
TYPES: BEGIN OF ty_s_tablecontrol,
idemp TYPE ZEMPLEADOS-IDEMP,
fecha TYPE ZDATGEN-FECHA,
nombr TYPE ZEMPLEADOS-NOMBR,
apel1 TYPE ZEMPLEADOS-APEL1,
apel2 TYPE ZEMPLEADOS-APEL2,
mark TYPE C,
delete TYPE C,
END OF ty_s_tablecontrol.
  1. Definir un tipo tabla según la estructura anteriormente creada.
1
TYPES ty_t_tablecontrol TYPE STANDARD TABLE OF ty_s_tablecontrol.
  1. Definir una variable para la tabla interna y su área de trabajo.
1
2
DATA wa_tablecontrol TYPE ty_s_tablecontrol.
DATA it_tablecontrol TYPE ty_t_tablecontrol.

Uso del Wizard

En este momento ya tenemos todos los elementos básicos para usar el Wizard y mostrar nuestro Table Control.

Importante: Antes de usar el asistente hay que activar el programa por completo.

Crearemos una Dynpro de tipo normal y cuya pantalla siguiente sea esa misma Dynpro. Para ejecutar dicho asistente acudimos al Layout de nuestra Dynpro y pulsamos el botón Table Control mediante Wizard. Podemos realizar lo mismo sin el Asistente pero será trabajar de más y con mayores posibilidades de equivocarnos.

Para ejecutar el asistente tenemos usaremos el icono de la barra lateral del Layout.

Insertar un Table control con el asistente

El primer paso del asistente pulsaremos el botón Continuar. De momento es sencillo.

Asistente - Inicio

En el segundo paso es darle un nombre a nuestro Table Control. Es importante elegir un nombre no muy largo por que el asistente creará subrutinas según ese nombre y si es muy largo se nos irá del número de caracteres máximo.

Nombre del Table Control

En el tercer paso nos pide si el Table Control va a manejar directamente una tabla del diccionario de datos o una tabla interna. Si seleccionamos la primera opción, con este Table Control vamos a modificar directamente el contenido de una tabla de la base de datos. No es necesario decir que esto puede ser un poco peligroso.

Nombre de la tablaSi seleccionamos la segunda opción deberemos indicar la tabla interna que va a gestionar el Table Control  y el área de trabajo que usaremos. Estos datos son las variables que creamos anteriormente.

En el cuarto paso seleccionaremos las filas que queremos mostrar en la tabla. Dejaremos sin seleccionar el campo que hemos creado para guardar las filas seleccionadas.

Asistente - Definición de columnasEn el siguiente paso seleccionaremos si la tabla será sólo de salida, es decir, no se podrán hacer modificaciones sobre el contenido de la tabla, de Entrada, que entonces permitirá realizar modificaciones sobre el contenido y crear nuevas líneas. Hay que tener en cuenta que esta configuración se podrá modificar posteriormente accediendo a la configuración desde el Layout o mediante código activando o desactivando la entrada de datos en el campo o la columna. Esto ya lo veremos posteriormente. En este ejemplo crearemos el Table Control de tipo Entrada y añadiremos la columna reservada en los pasos anteriores para el Campo columna de selección.  Habilitaremos la selección múltiple si queremos que esté disponible al usuario.

Asistente - Atributos del Table ControlEn el siguiente paso nos solicita que botones estándar tiene que crear para nuestro Table Control. Seleccionaremos todas las casillas.

Asistente - Selección de funciones

En este penúltimo paso indicaremos el nombre de los includes que usamos en nuestro código y donde el asistenta introducirá las partes de código necesarias. Si seguimos la nomenclatura estándar tendremos los siguientes includes para cada caso:

  • *TOP – Definiciones de datos.
  • *O01 – PBO.
  • *I01 – PAI.
  • *F01 – Subrutinas.

Asistente - Especificación de includesEl último paso no sindicará las acciones que se van a realizar. Pulsaremos sobre finalizar y el asistente creará el código necesario.

Asistente - Finalizar

En las siguientes secciones analizaremos el código para comprenderlo.

Código generado por el asistente en el PBO

En la pestaña de la lógica de proceso se ha creado el siguiente código para el PBO.

1
2
3
4
5
6
7
8
9
    MODULE TC_VAC_CHANGE_TC_ATTR.
    MODULE TC_VAC_CHANGE_COL_ATTR.
    LOOP AT   IT_TABLECONTROL
         INTO WA_TABLECONTROL
         WITH CONTROL TC_VAC
         CURSOR TC_VAC-CURRENT_LINE.
      MODULE TC_VAC_GET_LINES.
      MODULE TC_VAC_CHANGE_FIELD_ATTR.
    ENDLOOP.

Ciertas líneas aparecerán comentadas pero en este manual expondremos para que se utilizara cada uno de los módulos.

Dentro del módulo TC_VAC_CHANGE_TC_ATTR de la línea 1 pondríamos el código para cambiar los atributos del Table Control por completo.

Dentro del módulo TC_VAC_CHANGE_COL_ATTR de la línea 2 pondríamos el código para cambiar los atributos de las columnas del Table Control.

Desde la línea 3 hasta la 9 hay un bucle que recorre cada una de las líneas de la tabla interna. Será en este bucle donde se realizarán las diferentes acciones referentes a modificar el aspecto de cada una de las celdas de la tabla como por ejemplo permitir o no la modificación de esa celda. Dentro del módulo TC_VAC_CHANGE_FIELD_ATTR será el punto más idóneo para realizar esas tareas.  En el siguiente trozo de código se puede ver un ejemplo de lo que podemos realizar.

1
2
3
4
5
6
7
8
9
10
    MODULE TC_VAC_CHANGE_FIELD_ATTR OUTPUT.
      LOOP AT SCREEN.
        IF screen-name = 'WA_TABLECONTROL-FECHA'.
          IF WA_TABLECONTROL-FECHA > sy-datum.
            screen-input = '1'.
          ENDIF.
        ENDIF.
        MODIFY SCREEN.
      ENDLOOP.
    ENDMODULE.

En el anterior código, se activan las casillas de la tabla que contengan una fecha mayor a la del día de hoy. En la línea 2 se realiza un bucle sobre los campos de una fila de la tabla. En la línea 3 se comprueba si el capo actual de la ventana es el que no s interesa y en la línea 4 se comprueba la condición sobre ese campo. Si la condición se cumple, en la línea 5 se cambia la propiedad de la ventana que indica si el campo está disponible para operaciones de entrada y salida. Finalmente en la línea 8 se realiza la modificación de los cambios que se han realizado en la ventana.

Código generado por el asistente en el PAI

En la pestaña de la lógica de proceso se ha creado el siguiente código para el PBO.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    LOOP AT IT_TABLECONTROL.
        CHAIN.
          FIELD WA_TABLECONTROL-IDEMP.
          FIELD WA_TABLECONTROL-FECHA.
          FIELD WA_TABLECONTROL-NOMBR.
          FIELD WA_TABLECONTROL-APEL1.
          FIELD WA_TABLECONTROL-APEL2.
          MODULE TC_VAC_MODIFY ON CHAIN-REQUEST.
        ENDCHAIN.
        FIELD WA_TABLECONTROL-MARK
          MODULE TC_VAC_MARK ON REQUEST.
    ENDLOOP.
    MODULE TC_VAC_USER_COMMAND.
    MODULE TC_VAC_CHANGE_TC_ATTR.
    MODULE TC_VAC_CHANGE_COL_ATTR.

En la linea 8 se hace la llamada al módulo que realizará la modificación en la tabla interna de los cambios que se hayan realizado en la ventana. En este paso sería importante antes de realizar dicha modificación, comprobar la validez de los datos introducidos en la Dynpro Como en el siguiente ejemplo:

1
2
3
4
5
6
7
8
9
   MODULE TC_VAC_MODIFY INPUT.
     IF WA_TABLECONTROL-FECHA > sy-datum.
       MODIFY IT_TABLECONTROL
         FROM WA_TABLECONTROL
         INDEX TC_VAC-CURRENT_LINE.
     ELSE.
       MESSAGE 'Fecha anterior a la actual' TYPE 'W'.
     ENDIF.
   ENDMODULE.

Como vemos si la fecha solicitada es  mayor que la actual (línea 2) se realiza la modificación. Si es anterior se muestra el mensaje de error. Es importante notar que al estar el módulo dentro de un CHAIN … ENDCHAIN si se produce un mensaje de error en el módulo se regresa a la Dynpro y se activarán únicamente para modificación los campos declarados con la clausula FIELD .

En la línea 13 le realiza una llamada al módulo TC_VAC_USER_COMMAND. Este módulo es el encargado de gestionar los botones propios del Table Control  para insertar, borrar, avanzar o retroceder paginas o filas, marcar o desmarcar  una fila, etc.

Dentro del módulo TC_VAC_USER_COMMAND se llama a la subrutina USER_OK_TC que es la encargada de comprobar cual es el botón que ha sido pulsado y encaminar la ejecución a la subrutina encargada de la lógica de ese botón. En el siguiente diagrama se puede ver dicha lógica.

Flujo de la lógica de control para los botones del Table Control

El código estándar funciona para la mayoría de las necesidades, pero si por ejemplo necesitamos que la inserción de nuevas filas en la tabla ciertos campos se completen por defecto, necesitaremos modificar el código de la subrutina FCODE_INSERT_ROW.

Etiquetas: , , , , , , , ,

Select Options en tu WebDynpro.

Jueves, 4 de marzo de 2010

El objetivo que perseguimos es obtener una pantalla de selección como la que sigue:

Resultado Final SELECT-OPTIONS en WebDynpro.

Resultado Final SELECT-OPTIONS en WebDynpro.

En esta pantalla se han eliminado las opciones de selección extendidas pero estas salen por defecto y solo tendrás que eliminar unos parámetros para que aparezcan.

Como usar el componente WDR_SELECT_OPTIONS

El primer paso será indicar el uso del componente. Esto se hace en la vista del componente mediante los botones añadir y eliminar uso. Para este ejemplo usaremos tambien SALV_WD_TABLE para mostrar la tabla de materiales. El nombre que le pongamos en la columna “Component Use” será importante ya que se usará posteriormente.

Vista de Componente.

Vista de Componente.

En nuestro component controller también indicaremos el uso del componente para manejar select options.

Vista del Component Controller

Vista del Component Controller

En nuestra ventana principal crearemos un contenedor de vistas (ViewContainerUIElement) para nuestro conjunto de campos de selección.

Vista MAIN

Vista MAIN

En la vista de la ventana incrustaremos la vista WND_SELECTION_SCREEN que nos facilita el componente WDR_SELECT_OPTIONS. Esto es así por que en un mismo componente se pueden incluir varios campos de selección además de campos de entrada simples (Input Fields, Check Boxes, etc.).

Vista de Ventana

Vista de Ventana

En este momento tenemos la vista vacia, y tenemos que añadir los campos de selección que queramos. Esto se hace mediante 2 métodos que proporciona el componente en la interfaz if_wd_select_options. El primero crea la tabla donde se guardan los rangos de selección:

lt_range_matnr = lr_helper->create_range_table( i_typename = 'NOMBRE_TIPO_DATOS' ).

El segundo crea los campos de selección en la vista:

lr_helper->add_selection_field( i_id = 'NOMBRE_CAMPO'
it_result = lt_range_matnr ).

En esta función se han puesto los parámetros imprescindibles para crear un SELECT-OPTIONS como en las Dynpro ABAP, pero hay además otros parámetros que pueden resultar útiles como:

  • i_no_extension = [abap_true |abap_false] Elimina la posibilidad de crear una tabla de selecciones múltiple.
  • i_no_complex_restrictions = [abap_true |abap_false] Elimina la posibilidad de cambiar el operador relacional.

Estas tareas se realizarán en el método WDDOINIT de nuestro component controller, que es donde hemos declarado el uso del componente de sap para manejar select options, por lo que para nuestro ejemplo quedará como sigue:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
METHOD wddoinit .
 
  DATA lr_select_options TYPE REF TO iwci_wdr_select_options.
  DATA lr_helper TYPE REF TO if_wd_select_options.
  DATA lt_range_matnr TYPE REF TO data.
  DATA lt_range_matkl TYPE REF TO data.
  DATA lt_range_mtart TYPE REF TO data.
  DATA lr_comp_usage TYPE REF TO if_wd_component_usage.
  DATA lv_otr_text type STRING.
 
*this code is to instantiate the component wdr_select_options
  lr_comp_usage = wd_this->wd_cpuse_select_options( ).
  IF lr_comp_usage->has_active_component( ) IS INITIAL.
    lr_comp_usage->create_component( ).
  ENDIF.
 
* Call the interface controller method init_selection_screen to get the helper class
  lr_select_options = wd_this->wd_cpifc_select_options( ).
  lr_helper = lr_select_options->init_selection_screen( ).
 
* Set global options.
  lr_helper->set_global_options(
                              i_display_btn_cancel  = abap_false
                              i_display_btn_check   = abap_false
                              i_display_btn_reset   = abap_false
                              i_display_btn_execute = abap_false ).
 
*Use the helper class to create a range table for the data elements MATNR, MATKL and MTART.
  lt_range_matnr = lr_helper->create_range_table( i_typename = 'MARA-MATNR' ).
  lt_range_matkl = lr_helper->create_range_table( i_typename = 'MARA-MATKL' ).
  lt_range_mtart = lr_helper->create_range_table( i_typename = 'MARA-MTART' ).
 
*Add a Selection Screen Field
  lr_helper->add_selection_field( i_id = 'SO_MATNR'
                                  it_result = lt_range_matnr
                                  i_no_extension = abap_true             " Supress the multiple selection option.
                                  i_use_complex_restriction = abap_true  " Supress the relational operator button.
                                  ).
  lr_helper->add_selection_field( i_id = 'SO_MATKL'
                                  it_result = lt_range_matkl
                                  i_value_help_structure = 'MARA'        " Search help.
                                  i_value_help_structure_field = 'MATKL'
                                  i_no_extension = abap_true             " Supress the multiple selection option.
                                  i_use_complex_restriction = abap_true  " Supress the relational operator button.
                                  ).
  lr_helper->add_selection_field( i_id = 'SO_MTART'
                                  it_result = lt_range_mtart
                                  i_value_help_mode = '0'
                                  i_value_help_structure = 'MARA'        " Search help.
                                  i_value_help_structure_field = 'MTART'
                                  i_no_extension = abap_true             " Supress the multiple selection option.
                                  i_use_complex_restriction = abap_true  " Supress the relational operator button.
                                  ).
ENDMETHOD.

Es posible crear todo tipo de campos de entrada mediante el método add_parameter_field. Por ejemplo, para crear un check box:

lr_helper->add_parameter_field( i_id = 'NOMBRE_CAMPO'
                                  i_as_checkbox = abap_true
                                  i_description = lv_otr_text
                                  ).

En este momento ya tenemos una pantalla donde se muestra un conjunto de campos de selección para nuestras necesidades. Nos queda resolver la necesidad de chequear la corrección de los datos introducidos por el usuario y la obtención de estos para generar la consulta.

Para el primer paso, el componente ofrece el método check_all_selection_fields que devuelve el número de errores que se han producido. Posteriormente podemos usar un IF para comprobar si retorna 0 errores y continuar con el proceso o cancelar. El componente mostrará automáticamente los errores en la pantalla.

 CALL METHOD lr_helper->check_all_selection_fields
    IMPORTING
      e_num_error_msgs   = lv_num_error_msgs
*      e_num_warning_msgs = lv_num_warning_msgs
*      e_num_info_msgs    = lv_num_info_msgs
      .
 
  IF lv_num_error_msgs EQ 0
*     lv_num_warning_msgs eq 0
*     lv_num_info_msgs eq 0
  .
* No errors
  ELSE.
* At least one error.
  ENDIF.

El siguiente paso es obtener la tabla con las opciones de selección que ha realizado el usiario. Esto se hará de la siguiente forma:

  DATA so_matnr TYPE REF TO data.
  FIELD-SYMBOLS  TYPE ANY TABLE.
  so_matnr = lr_helper->get_range_table_of_sel_field( i_id = 'SO_MATNR' ).
  ASSIGN so_matnr->* TO .

El FIELD-SYMBOL es necesario para usarlo posteriormente en la consulta a la base de datos (sentencia SELECT … IN …) ya que de otra forma se producira un error de compilación.

Para realizar el chequeo de los campos que no son SELECT-OPTIONS y obtener sus valores, usaremos los métodos check_all_parameter_fields y get_value_of_parameter_field de la interfaz if_wd_select_options.

Estos metodos se usarán en una acción asignada a un botón. Para el ejemplo, hemos creado una acción MOSTRAR asignandola a su correspondiente botón. El código completo quedará como sigue:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
METHOD onactionmostrar .
 
  DATA lo_el_context TYPE REF TO if_wd_context_element.
  DATA ls_context TYPE wd_this->element_context.
  DATA lv_limit TYPE wd_this->element_context-limit.
 
  DATA lo_nd_alv_table TYPE REF TO if_wd_context_node.
  DATA lt_alv_table TYPE wd_this->elements_alv_table.
 
  DATA so_matnr TYPE REF TO data.
  DATA so_matkl TYPE REF TO data.
  DATA so_mtart TYPE REF TO data.
*  DATA limit TYPE REF TO data.
  FIELD-SYMBOLS <so_matnr> TYPE ANY TABLE.
  FIELD-SYMBOLS <so_matkl> TYPE ANY TABLE.
  FIELD-SYMBOLS <so_mtart> TYPE ANY TABLE.
 
  DATA lr_select_options TYPE REF TO iwci_wdr_select_options.
  DATA lr_helper TYPE REF TO if_wd_select_options.
  DATA lr_comp_usage TYPE REF TO if_wd_component_usage.
  DATA lt_fields TYPE if_wd_select_options=>tt_selection_screen_item.
  FIELD-SYMBOLS <ls_field> TYPE if_wd_select_options=>t_selection_screen_item.
  DATA: lv_num_error_msgs TYPE i,
        lv_num_warning_msgs TYPE i,
        lv_num_info_msgs TYPE i.
 
* Get report table from context.
  lo_nd_alv_table = wd_context->get_child_node( name = wd_this->wdctx_alv_table ).
 
  lr_comp_usage = wd_this->wd_cpuse_select_options( ).
  IF lr_comp_usage->has_active_component( ) IS INITIAL.
    lr_comp_usage->create_component( ).
  ENDIF.
 
  lr_select_options = wd_this->wd_cpifc_select_options( ).
 
  lr_helper = lr_select_options->init_selection_screen( ).
 
* check correction of selection fields.
  CALL METHOD lr_helper->check_all_selection_fields
    IMPORTING
      e_num_error_msgs   = lv_num_error_msgs
*      e_num_warning_msgs = lv_num_warning_msgs
*      e_num_info_msgs    = lv_num_info_msgs
      .
 
  IF lv_num_error_msgs EQ 0
*     lv_num_warning_msgs eq 0
*     lv_num_info_msgs eq 0
  .
    lr_helper->get_selection_screen_items(
                  IMPORTING et_selection_screen_items = lt_fields ).
 
* Retrieve the data from the select option
    so_matnr = lr_helper->get_range_table_of_sel_field( i_id = 'SO_MATNR' ).
    ASSIGN so_matnr->* TO <so_matnr>.
 
    so_matkl = lr_helper->get_range_table_of_sel_field( i_id = 'SO_MATKL' ).
    ASSIGN so_matkl->* TO <so_matkl>.
 
    so_mtart = lr_helper->get_range_table_of_sel_field( i_id = 'SO_MTART' ).
    ASSIGN so_mtart->* TO <so_mtart>.
 
*    limit = lr_helper->get_value_of_parameter_field( i_id  = 'LIMIT' ).
 
    lo_el_context = wd_context->get_element( ).
    lo_el_context->get_attribute(
      EXPORTING
        name =  `LIMIT`
      IMPORTING
        value = lv_limit ).
 
    IF lv_limit = abap_true.
      SELECT m~matnr d~maktx txtm~mtbez txtt~wgbez m~ernam
        INTO CORRESPONDING FIELDS OF TABLE lt_alv_table
        FROM  ( ( ( mara AS m
                LEFT OUTER JOIN makt AS d ON m~matnr = d~matnr AND d~spras = sy-langu  )
                LEFT OUTER JOIN t134t AS txtm ON m~mtart = txtm~mtart AND txtm~spras = sy-langu )
                LEFT OUTER JOIN t023t AS txtt ON m~matkl = txtt~matkl AND txtt~spras = sy-langu )
       UP TO 500 ROWS
       WHERE m~matnr IN <so_matnr>
         AND m~mtart IN <so_mtart>
         AND m~matkl IN <so_matkl>
       .
    ELSE.
      SELECT m~matnr d~maktx txtm~mtbez txtt~wgbez m~ernam
       INTO CORRESPONDING FIELDS OF TABLE lt_alv_table
       FROM  ( ( ( mara AS m
               LEFT OUTER JOIN makt AS d ON m~matnr = d~matnr AND d~spras = sy-langu  )
               LEFT OUTER JOIN t134t AS txtm ON m~mtart = txtm~mtart AND txtm~spras = sy-langu )
               LEFT OUTER JOIN t023t AS txtt ON m~matkl = txtt~matkl AND txtt~spras = sy-langu )
      WHERE m~matnr IN <so_matnr>
        AND m~mtart IN <so_mtart>
        AND m~matkl IN <so_matkl>
      .
    ENDIF.
    IF sy-dbcnt > 0.
      lo_nd_alv_table->bind_table( new_items = lt_alv_table set_initial_elements = abap_true ).
      wd_this->fire_to_alv_plg(
      ).
    ELSE.
      wd_this->fire_to_no_data_plg(
      ).
    ENDIF.
  ENDIF.
ENDMETHOD.

Completando el ejemplo.

A partir de aqui, el resto del tutorial nos centramos en completar el ejemplo para poder mostrar el resultado de la consulta en un ALV. Si solo tienes interes en usar el componente para crear los SELECT_OPTIONS puedes terminar de leer aqui.

Para almacenar los datos que se mostrarán en el ALV, crearemos un nodo en el controlador del componente (Component Controller) que contendrá los campos que deseemos mostrar. Para este ejemplo, realizaremos un informe sobre la tabla de materiales en la que mostraremos el numero de material, la descripción el grupo y el tipo en el idioma de inicio de sesión:

  • mara-matnr
  • makt-maktx
  • t134t-mtbez
  • t023t-wgbez

En la vista del controlador ALV asociaremos el nodo creado en nuestro controlador con el nodo DATA que dispone el controlador del ALV. Esto hace que se muestren los datos en la tabla del ALV.

Mapeo datos ALV

Mapeo datos ALV

También deberemos crear los conectores de salida y los de entrada para las diferentes vistas y asociarlos. En la siguiente captura de pantalla se pueden apleciar los diferentes conectores creadoy y su conexión.

Conectores

Conectores

Etiquetas: , , , , , ,

Cómo cambiar tu blog en WordPress a un nuevo servidor

Lunes, 19 de octubre de 2009

El mayor problema que nos encontramos al pretender cambiar la dirección web de nuestro blog es que en WordPress usa direcciones absolutas en ciertos parámetros que almacena en la base de datos en lugar de direcciones relativas. Este artículo le mostrara como cambiar de forma sencilla estas direcciones absolutas para que apunten al nuevo dominio o directorio dentro del mismo dominio.

El primer paso que hay que realizar es mover todo el contenido de tu instalación de WordPress al nuevo destino, ya sea un nuevo directorio de tu antiguo servidor o tu nuevo servidor. Una vez completado este paso, cambiaremos una serie de opciones en la tabla OPTIONS que indican la dirección de nuestro blog. Para ello ejecutaremos el siguiente comando SQL en nuestra base de datos:

UPDATE wp_options
   SET option_value = REPLACE(option_value, 'http://www.antiguo-dominio.com', 'http://www.nuevo-dominio.com')
 WHERE option_name = 'home' OR option_name = 'siteurl';

Posteriormente, necesitarás arreglar los enlaces que se encuentran dentro de los artículos y las páginas de tu blog a la nueva dirección. Para realizar esto hay que ejecutar el siguiente comando SQL:

UPDATE wp_posts
   SET guid = REPLACE(guid, 'http://www.antiguo-dominio.com','http://www.nuevo-dominio.com');

En el caso de que hayas hecho enlaces desde tus propios contenidos a direcciones absolutas (es decir direcciones en las que indicas el el nombre del servidor por completo) además necesitarás ejecutar también:

UPDATE wp_posts
   SET post_content = REPLACE(post_content, 'http://www.old-domain.com', 'http://www.new-domain.com');

Tras realizar estas operaciones, ya podrás entrar en tu blog usando tu nueva dirección. En el caso de que no funcionen los Permalinks deberás generar un nuevo el fichero .htaccess mediante el interfaz de administración de WordPress acudiendo al menú ‘Opciones/Permalinks’ en el Tablero.

En este punto ya tendrás tu blog funcionando correctamente en la nueva dirección web, pero tendrás que indicar al mundo que has cambiado de servidor o de directorio dentro de tu servidor antiguo. Esto lo puedes realizar de forma sencilla mediante reglas en htaccess. Por ejemplo, para el caso de que cambies del directorio raíz en tu mismo servidor a un nuevo directorio, es decir desde http://www.midominio.com/ a http://www.midominio.com/blog puedes usar la siguiente redirección en htaccess:

redirectMatch 301 ^(.*)$ http://www.midominio.com/blog$1

Fuente: My Digital Life

Más información sobre redirectMatch.

Etiquetas: , , , , ,

Configurar el modo debug en OC4J

Jueves, 26 de marzo de 2009

Mediante la variable de entorno OC4J_JVM_ARGS podemos añadir parámetros a la máquina virtual java al iniciar el servidor.

Añadiendo a la variable de entorno OC4J_JVM_ARGS el sigueite valor

-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=4000

se iniciará el servidor de aplicaciones en modo debug y podremos usar el Eclipse para conectar el debug en el puerto 4000.

Con este método, se puede agregar cuaquier tipo de parámetros que deseemos indicar a la máquina virtual de java. Por ejemplo podemos aumentar la memoria reservada para el cargador de clases:

-XX:PermSize=512m -XX:MaxPermSize=512m

Y para la para la pila:

-Xms128m -Xmx1024m

Etiquetas: , , , ,

Eliminar la validación del servidor en conexiones HTTPS

Lunes, 23 de marzo de 2009

Al realizar una conexión segura (https), se valida que el servidor es quien dice ser mediante el envio de un certificado que el cliente tiene que conocer. Si lo que quieres es confiar siempre en todos los certificados, este código te puede ser util.

// Crear un gestor de certificados que no valida si el certificado esta en el
// almacen de certificados de confianza.
TrustManager[] trustAllCerts = new TrustManager[]{
   new X509TrustManager() {
      public java.security.cert.X509Certificate[] getAcceptedIssuers() {
         return null;
      }
 
      public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
      }
 
      public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
      }
   }
};
 
// Instalar el gestor de certificados.
try {
   SSLContext sc = SSLContext.getInstance("SSL");
   sc.init(null, trustAllCerts, new java.security.SecureRandom());
   HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
}

Etiquetas: , , , , ,

Maquetación con etiquetas div y css

Miércoles, 11 de marzo de 2009

El objetivo es conseguir una maquetación como la de esta imagen:

Maquetación mediante CSS

Como vemos en la imagen la estructura de nuestra página se asemeja mucho a lo que pudiera ser una tabla, por lo que lo primero que se nos puede ocurrir para crear esta estructura es usar la etiqueta html <head>.
Si queremos seguir los últimos estándares web, sólo podremos usar la etiqueta <head> para mostrar contenido que tenga que ser tabulado. Por ejemplo, podremos usar una tabla para esto:

Elemento Cantidad
Pan 1
Filetes 2
leche 6

Pero nunca para hacer la maquetacón de nuestra página. Por lo tanto, el código html que vamos a usar es el siguiente:

<html>
    <head>
    <title>Ejemplo</title>
    </head>
    <body id="body">
        <div id="contenedor">
            <div id="destacado">
            DESTACADO 1
            </div>
            <div id="destacado" class="scroll">
            DESTACADO 2
            </div>
            <div id="destacado">
            DESTACADO 3
            </div>
            <div id="cajas">
                <div id="caja">Caja 1</div>
                <div id="caja">Caja 2</div>
                <div id="caja">Caja 3</div>
                <div id="caja">Caja 4</div>
                <div id="caja">Caja 5</div>
                <div id="caja">Caja 6</div>
                <div id="caja">Caja 7</div>
                <div id="caja">Caja 8</div>
                <div id="caja">Caja 9</div>
                <div id="caja">Caja 10</div>
            </div>
        </div>
    </body>
</html>

Lo primero que vamos a hacer será centrar el contenedor general. Esto es tan sencillo como usar los siguiente estilos en nuestro CSS:

#contenedor{
    background: red;
    margin:auto;  /* para centrar el contenedor */
    width: 600px; /* es necesario dar un tamaño, sino
                  /* ocuparía toda la ventana y no se
                  /* va a centrar*/
}

Para que esto funcione en Internet Explorer 5.5 además hay que añadir text-align, por lo que el CSS final queda de la siguiente manera:

#body{
    background: black;
    text-align:center; /*para que en IE 5.5 salga centrado*/
}
 
#contenedor{
    background: red;
    margin:auto;      /* para centrar el contenedor */
    width: 600px;     /* es necesario dar un tamaño*/
                      /* sino ocuparía toda la ventana
                      /* y no se va a centrar*/
    text-align:left;  /* para eliminar la herencia en IE 5.5
                      /* y que los textos dentro del contenedor
                      /* no salgan centrados.*/
}

Para los cuadros verdes, lo único que tenemos que hacer es darle un margen y indicarle que tendrán un alto mínimo. Por defecto ya ocuparán todo el ancho disponible:

#destacado{
    margin: 0px 15px 15px 15px;
    height: 100px; /* le damos un alto mínimo*/
    background: green;
}

Y por último para las cajas azules lo que hay que hacer es:
Darle un ancho y alto al contenedor de esas cajas (marcado como 1 y 2).
Asignarle un ancho y alto a la caja (3 y 4)
Indicar que las capas se coloquen una a la derecha de la otra ocupando todo el espacio disponible (5).

#cajas{
    width: 600px;               /* 1 - le damos un ancho */
    height:220px;               /* 2 - le damos un alto */
    margin-top:10px;
}
#caja{
    width: 100px;               /* 3 */
    height: 100px;              /* 4 */
    float: left;                /* 5 - para que salga en formato de tabla*/
    margin: 0px 2px 10px 15px;
    background: blue;
}

Podéis descargar ejemplo de maquetación mediante CSS y etiquetas DIV. para probar.

En Excelsit somos profesionales en el diseño web.

Etiquetas: , , , , , , ,